对象、消息、运行时
理解“属性”的概念
属性的基础用法就不多叙述了
属性特质
@property (nonatomic,readwrite,copy) NSString *name;
属性拥有的特质分为4类
1、原子性
atomic
设置成员变量的@property属性时,默认为atomic,提供多线程安全。
在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:
{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}
nonatomic
禁止多线程,变量保护,提高性能。 atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic ,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。
开发iOS程序时,应该使用nonatomic属性,因为atomic属性会严重影响性能。
2、读/写权限
readwrite会自动生成getter/setter方法
readonly只有getter方法
3、内存管理语义
assign “设置方法”只会执行针对“纯量类型”(CGFloat&&NSIntger等)的简单赋值操作。
strong 此特质表明该属性定义了一种“拥有关系”。赋值时会保留新值,并释放旧值,然后再讲新值设置上去
weak 此特质表明该属性定义了一种“非拥有关系”。赋值时既不保留新值,也不释放旧值,同assign相似。然而属性所指的对象销毁时,属性值也会被nil;
unsafe_unretained此特质的语义和assign相同,但他适用于对象类型,该特质表达一种“非拥有关系”(“不保留”),当目标对象遭到销毁时,属性值不会被清空,与weak有区别。
copy所表达的所属关系与strong相似。然而设置方法并不保留新值,而是将其copy。当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示可修改字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值可能会在对象不知情的情况下遭到更改。
weak比 assign多了一个功能就是当属性所指向的对象消失的时候(也就是内存引用计数为0)会自动赋值为 nil,这样再向 weak修饰的属性发送消息就不会导致野指针操作crash。
weak如何自动置nil?
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
在对象内部尽量直接访问实例变量
1、访问实例变量不经过objc的方法派发(method dispatch),所以速度更快。在这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块内存。
2、直接访问实例变量,不会调用getter/setter方法
如果需要使用KVO或懒加载,还是得使用属性
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,应该通过属性来写。
在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
有时会使用惰性初始化技术配置某份数据,这种情况下,需要通过属性来读取数据。
理解“对象等同性”
NSObject协议中有两个用于判断等同性的关键方法:
-(Bool)isEqual:(id)object;
-(NSUInteger)hash;
若想检测对象的等同性,请提供“isEqual:”与hash方法。
相同的对象必须具有相同的hash码,但是两个hash码相同的对象却未必相同。
不要盲目的逐个监测每条属性,而是应该依照具体需求来制定检测方案。
编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。
理解objc_msgSend的作用
id returnValue = [someObject messageName:parameter];
someObject叫做receiver,messageName叫做selector,selector与参数合起来称为message。编译器看到消息后,将其转换为一条标准的C语言函数调用,如下
id returnValue = objc_msgSend(someObject,@selector(messageName:) parameter);
消息由receiver,selector及参数构成。给某对象“发送消息”也就相当于在该对象上调用方法。
发给某对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出对应的方法,并执行其代码。
理解消息转发机制
什么是消息转发?当对象接收到无法解读的消息后,就会启动消息转发机制。
消息转发分为两个阶段。第一阶段先征询接收者所属的类,看其是否能动态添加方法,已处理当前这个“未知的selector”,这叫做“动态方法解析”。第二阶段涉及“完整的消息转发机制”。如果运行期系统已经把第一阶段执行完了,那么接收者自己就无法再以动态新增方法的手段来响应包含该selector的消息了。此时运行期系统就会请求接收者以其他手段来处理与消息相关的方法调用。细分为两步:首先,让接收者看看有没有其他对象能处理这条消息。如果有,则运行期系统会把消息转给那个接收者,于是消息转发结束。如果没有这个“备援接收者”,则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节封装到NSInvocation对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。
消息转发机制流程图若对象无法响应某个selector,则进入消息转发流程。
通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中。
对象可以将其无法解读的某些selector转交给其他对象处理。
经过上述两步后,如果还是没办法处理selector,那就启动完整的消息转发机制。
用method swizzling调试黑盒方法
在runtime中,可以向类中新增或替换selector所对应的方法实现。
使用另一份实现来替换原有的方法实现,这道工序叫做method swizzling,开发者常用此技术向原有视线中添加功能。
一般来说,只有调试程序的时候才需要在runtime中修改方法实现,这种做法不宜滥用。
网友评论