关键词、关键概念
#import
跟 #include
有什么区别, @class
呢,#import ""
跟 #import <>
有什么区别
-
#include
在C 语言中会引入一个头文件,但是可能出现交叉编译,#import
,在OC 中引入自己创建的头文件(#import ""
),或者系统框架(#import <>
),#import
不会出现交叉编译 -
@class
的作用是告诉编译器有@class后面的内容是一个类名。只是告诉编译器存在这么一个类,类具体包含哪些方法,属性和变量的并没有告诉编译器。一般在类的头文件中使用 @class来引入其他类。 -
#import<>
用于对系统文件的引用,编译器会在系统文件目录中去查找文件 -
#import ""
用于对自定义的文件的引用,编译器首先回去用户目录下查找,然后去安装目录,最后去系统目录中查找文件。
描述一下KVO 和 KVC
- Key-Value Observing (简写为KVO) ,当指定的对象的属性被修改了,允许对象接受到通知的机制。每次指定的被观察对象的属性被修改的时候, KVO都会自动的去通知相应的观察者。
- Key-Value Coding(简写为KVC),它是一种可以直接通过字符串的名字 (key)来访问类属性的机制。而不是通过调用
setter,getter
方法访问。
类目和继承的区别
- 类目是对方法的扩展,不能添加成员变量。继承可以在原来父类的成员变量的基础上,添加新的成员变量
- 类目只能添加新的方法,不能修改和删除原来的方法。继承可以增加、修改和删除方法。
- 类目不提倡对原有的方法进行重载。继承可以通过使用 super对原来方法进行重载。
- 类目可以被继承,如果一个父类中定义了类别,那么其子类中也会继承此类别。
什么是懒加载? 在使用懒加载时的注意事项是什么?
所谓的懒加载指的是延迟创建对象,只有当需要的时候才创建对象。在真正开发的过程中其实懒加载就是重写 getter
方法。在getter
方法的内部,实现对象的创建,如果对象为 nil
才创建,如果不为nil
,直接返回对象。在真正使用懒加载时需要注意的是当第一次使用对象时,需要调用 self.
因为只有这样才能调用对应的 getter
方法,对象才会被创建。
简述对UIView 、UIWindow、 CALayer的理解
-
CALayer
是图层类,本身可以显示的,但是不能响应事件。 -
UIView
是iOS
系统中界面元素的基础,所有的界面元素都继承自它。事件的处理由它来执行,但是显示其实是由其对应的layer
层来操作的,UIView
内嵌了一个layer
,layer
显示内容,UIView
本身增加了事件处理的功能。 -
UIWindow
继承自UIView
,主要的作用是作为窗口呈现其他的视图。而且一个应用程序一般情况下只有一个窗口。
id声明的对象有什
id
是任意对象类型的,不能表示基本类型。id
类型是通用指针类型,因为通过指针,也就是内存地址来引用对象,所以可以将任意对象赋值给id
类型的对象。返回id
类型值的方法是返回指向内存中某对象的指针。然后可以将该值赋给任何对象变量(强制类型转换即可)。因为无论在哪里,对象总是携带它的isa
成员。所以即使将它存储在id
类型的通用对象变量中,也总是可以确定它的真实类型,id
是多态的一种体现
@synthesize和@dynamic 有什么区别?
-
@property
有两个对应的词,一个是@synthesize
,一个是@dynamic
。如果@synthesize
和@dynamic
都没写,那么默认的就是@syntheszie var = _var
-
@synthesize
的语义是如果你没有手动实现setter
方法和getter
方法,那么编译器会自动为你加上这两个方法。 -
@dynamic
告诉编译器:属性的setter
与getter
方法由用户自己实现,不自动生成。(当然对于readonly
的属性只需提供getter
即可)。 - 假如一个属性被声明为
@dynamic var
,然后你没有提供@setter
方法和@getter
方法,编译的时候没问题,但是当程序运行到instance.var = someVar
,由于缺setter
方法会导致程序崩溃;或者当运行到someVar = instance.var时
,由于缺getter
方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。 -
@dynamic
可用于在分类中添加属性(需要用到objc_getAssociatedObject
和objc_setAssociatedObject
函数)。
在Category 中本身不允许为已有的类添加新的属性或者成员变量,有没有其他的方法可以在 category中添加属性或者是成员变量
- 一种方法就是使用
runtime.h
中的objc_getAssociatedObject
和objc_setAssociatedObject
来访问和生成关联对象。例如为NSObject
添加一个类目,分类中添加一个属性。代码如下所示:
NSObject+Test.h
文件
#import <Foundation/Foundation.h>
@interface NSObject (Test)
@property (nonatomic, strong) NSString *test;
@end
NSObject+Test.m
文件
#import “NSObject+Test.h"
#import <objc/runtime.h>
static const void *instanceNameKey = &instanceNameKey;
@implementation NSObject (Test)
@dynamic test;
- (NSString *)test
{
return objc_getAssociatedObject(self, instanceNameKey);
}
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, instanceNameKey, test, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
sizeof 和strlen 的区别和联系
-
sizeof(...)
是运算符,在头文件中typedef
为unsigned int
,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。
—它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。由于在编译时计算,因此sizeof
不能用来返回动态分配的内存空间的大小。实际上,用sizeof
来返回类型以及静态分配的对象、结构或数组所占的空间,返回值跟对象、结构、数组所存储的内容没有关系。 -
strlen(...)
是函数,要在运行时才能计算。参数必须是字符型指针(char *)
。当数组名作为参数传入时,实际上数组就退化成指针了。
—它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL
。返回的长度大小不包括NULL
。 - 区别和联系
1.sizeof
操作符的结果类型是size_t
,它在头文件中typedef
为unsigned int
类型。该类型保证能容纳实现所建立的最大对象的字节大小。
2.sizeof
是运算符,strlen
是函数。
3.sizeof
可以用类型做参数,strlen
只能用char*
做参数,且必须是以'\0'
结尾的。sizeof
还可以用函数做参数,比如:short f()
;printf("%d\n",sizeof(f()))
; 输出的结果是sizeof(short)
,即2 。
4. 数组做sizeof
的参数不退化,传递给strlen
就退化为指针了。
5. 大部分编译程序 在编译的时候就把sizeof
计算过了 是类型或是变量的长度这就是sizeof(x)
可以用来定义数组维数的原因char str[20]="0123456789"
;int a=strlen(str); // a=10
;int b=sizeof(str); //b=20
;
6.strlen
的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小。
7.sizeof
后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof
是个操作符不是个函数。
8. 当适用了于一个结构类型时或变量,sizeof
返回实际的大小,当适用一静态地空间数组, sizeof 归还全部数组的尺寸。sizeof
操作符不能返回动态地被分派了的数组或外部的数组的尺寸
const
意味着"readonly"
,关键字const
什么含义? 下面的声明都是什么意思 ?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
- 前两个的作用是一样,
a
是一个常整型数 - 第三个意味着
a
是一个指向常整型数的指针 (也就是 ,整型数是不可修改的 ,但指针可以 ) - 第四个意思
a
是一个指向整型数的常指 针 (也就是说 ,指针指向的整型数是可以修改的 ,但指针是不可修改的 ) - 最后一个意 味着
a
是一个指向常整型数的常指针 (也就是说 ,指针指向的整型数是不可修改的 ,同时指针也是不可修改的 )。欲阻止一个变量被改变 ,可以使用const
关键字。在定义该const
变量时 ,通常需要对它进 ⾏初始化, 因为以后就没有机会再去改变它了- 对指针来说, 可以指定指针本身为
const
, 也可以指定指针所指的数据为const
, 或二者同时指定为const
; - 在一个函数声明中,
const
可以修饰形参 ,表明它是一个输入参数 ,在函数内部不能改变其值 ; - 对于类的成员函数,若指定其为
const
类型, 则表明其是一个常函数 ,不能修改类的成员变量 ; - 对于类的成员函数,有时候必须指定其返回值为
const
类型, 以使得其返回值不为“左值”
- 对指针来说, 可以指定指针本身为
nil,NSNULL,NULL
区别
-
nil
定义一个实例(instance)为空, 指向OC中对象的空指针,是对 Objective-Cid
对象赋空值,对于 Objective-C 集合类对象比如数组对象,字典对象,当我们不需要再使用他们的时候,对他们release
的同时最好也把他们赋值为nil
,这样确保安全性,如果不赋值nil
,可能导致程序崩溃 -
NSNull
类定义了一个单例对象用于表示集合对象的空值。集合对象无法包含 nil作为其具体值,如NSArray、NSSet
和NSDictionary
。相应地,nil
值用一个特定的对象NSNull
来表示。NSNull
提供了一个单一实例用于表示对象属性中的的nil
值。默认的实现方法中,dictionaryWithValuesForKeys:
和setValuesForKeysWithDictionary:
自动地将NSNull1
和nil
相互转换,因此您的对象不需要进行NSNull
的测试操作。 -
NULL
可以用在C 语言的各种指针上 ,在 Objective-C里,nil
对象被设计来跟NULL
空指针关联的。他们的区别就是nil
是一个对象,而NULL
只是一个值。而且我们对于nil
调用方法,不会产生 `crash或者抛出异常。
NSDictionary 和NSMutableDictionary的区别,是有序还是无序(追问:可以排序吗?如何排序,最好代码实现)
NSDictionary
是不可变的对象,NSMutableDictionary
是可变对象,可以进行添加和删除操作。是无序的排序的话可以这样做:
- 获取所有的key
NSArray *myKeys = [myDictionary allKeys];
- 对key 进行排序
NSArray *sortedKeys = [myKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
- 按照key 获取对象
id firstObject = [myDictionary objectForKey: [sortedKeys objectAtIndex:0]];
keysSortedByValueUsingSelector/keysSortedByValueUsingComparator
通过使用指定SEL
或NSComarator
来对allKeys
进行排序,然后通过objectsForKeys
取出排序后的键-值(key-values)对。
内存中的栈和堆的区别是什么?哪些数据在栈上哪些数据在堆上?
- 管理方式: 对于栈来讲,是由编译器自动管理 ,无需我们手工控制 ;对于堆来说 ,释放工作由程序员控制 ,容易产生memory leak。
- 申请大小: 能从栈获得的空间较小 ,堆是向高地址扩展的数据结构 ,是不连续的内存区域。堆的大小受限于计算机系统中 有效的虚拟内存。由此可见 ,堆获得的空间比较灵活 ,也比较大。
- 碎片问题: 对于堆来讲,频繁的
new/delete
势必会造成内存空间的不连续 ,从而造成大量的碎片 ,使程序效率降低。对于栈来讲 ,则不会存在这个问题 ,因为栈是先进后出的队列 ,他们是如此的一一对应 ,以至于永远都不可能有一个内存块从栈中间弹出 - 分配方式: 堆都是动态分配的 ,没有静态分配的堆。栈有 2种分配方式 :静态分配和动态分配。静态分配是编译器完成 的 ,比如局部变量的分配。动态分配由 alloca函数进行分配, 但是栈的动态分配和堆是不同的 ,他的动态分配是由编译器 进行释放 ,无需我们手工实现。
- 分配效率: 栈是机器系统提供的数据结构 ,计算机会在底层对栈提供支持 :分配专门的寄存器存放栈的地址 ,压栈出栈 都有专门的指令执行 ,这就决定了栈的效率比较高。堆则是C/C++函数库提供的, 它的机制是很复杂的。
- 在函数体中定义的变量通常是在栈上,用
malloc, calloc, realloc
等分配内存的函数分配得到的就是在堆上。
浅复制和深复制的区别
浅层复制:只复制指向对象的指针 ,而不复制引用对象本身。
深层复制:复制引用对象本身。
意思就是说有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一 个内存资源,复制的只不过是一个指针,对象本身资源还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改。
深复制就好理解了 ,内存中存在了两份独立对象本身。
深浅拷贝前提是:是实现 NSCopying或者NSMutableCopying 协议。
深拷贝则对对象本身复制,同时对对象的属性也进行复制。
浅拷贝只是复制对象本身,对象的属性和包含的对象不做复制。
Foundation框架支持复制的类,默认是浅拷贝。其中对 Foundation中不可变的对象进行copy时作用相当于 retain。而如果是 mutablecopy时,无论对象是否可变,副本是可变的,并且实现了真正意义上的 copy。如果对可变对象进行copy,副本对象是不可变的,同样是真正意义上的 copy。
readwrite
,readonly
, assign
,retain
, copy
,nonatomic
属性的作用
@property
是一个属性访问声明,扩号内支持以下几个属性
1. getter=getterName
,setter=setterName
,设置 setter
与getter
的方法名
2. readwrite
,readonly
,设置可供访问级别
3. assign
,setter
方法直接赋值, 不进行任何retain
操作 ,为了解决原类型与环循引用问题
4. retain
,setter
方法对参数进行release旧值再 retain
新值, 所有实现都是这个顺序 (CC上有相关资料 )
5. copy
,setter
方法进行copy
操作,与 retain
处理流程一样, 先旧值release
,再 copy
出新的对象, retainCount
为1。这是为了减少对上下文的依赖而引入的机制。 copy
是在不希望a 和b共享一块内存时会使用到。 a和 b各自有自己的内存。
6.nonatomic
, 非原子性访问, 不加同步,多线程并发访问会提高性能。注意 ,如果不加此属性 ,则默认是两个访问方法都为原子型事务访问。锁被加到所属对象实例级。atomic
和nonatomic
用来决定编译器生成的 getter和setter 是否为原子操作。在多线程环境下 ,原子操作 是必要的 ,否则有可能引起错误的结果。
@property
中属性retain,copy,assgin
的含义分别是什么?有什么区别?将其转换成get/set
方法怎么做?有什么注意事项?
-
assign
: 简单赋值,不更改索引计数( Reference Counting)。使用assign
对基础数据类型 (NSInteger
,CGFloat
)和C数据类型(int
,float
,double
,char
, 等等) -
copy
: 建立一个索引计数为 1的对象,然后释放旧对象。使用copy
对NSString
retain
:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为 1。使用retain
对其他NSObject
和其子类retain
表示持有特性,setter
方法将传入参数先保留,再赋值,传入参数的retaincount
会+1
- (void)setInstance:(id)instance{
if (_instance != instance) {
[_instance release];
_instance = [instance retain];
}
}
-
copy
表示赋值特性,setter
方法将传入对象复制一份;需要完全一份新的变量时
- (void)setInstance:(id)instance{
if (_instance != instance) {
[_instance release];
_instance = [instance copy];
}
}
-
assign
是赋值特性,setter
方法将传入参数赋值给实例变量;仅设置变量时
- (void)setInstance:(id)instance{
if (_instance != instance) {
_instance = instance;
}
}
setValue 和setObject区别
在使用NSMutableDictionary
的时候经常会使用setValue: forKey:
与setObject: forKey:
,他们经常是可以交互使用的,代码中经常每一种的使用都有。
他们二者的区别就是
1. setObject:forkey:
中 value
是不能够为nil
的,不然会报错。setValue: forKey:
中value
能够为nil
,但是当value
为nil
的时候,会自动调用 removeObject:forKey:
方法
2. setValue: forKey:
中 key
的参数只能够是NSString
类型,而setObject: forKey:
的可以是任何类型
3. setObject:forKey:
方法NSMutabledictionary
特有的 ,而setValue: forKey:
方法是KVC(键-值编码)的主要方法。
使用圆角效果时为什么会出现卡顿效果 ,如何解决这个问题 ?
不要在滚动视图使用cornerRadius
或者mask
,添加
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
采取预先生成圆角图片,并缓存起来这个方法才是比较好的手段。预处理圆角图片可以在后台处理,处理完毕后缓存起来,再在主线程显示,这就避免了不必要的离屏渲染了。