发消息步骤
1.检查这个selector是不是要被忽略的,比如mac os开发,
有垃圾回收,就不考虑 retain,release这些函数
2.检测这个target是不是nil对象,
ObjC的特性允许对一个nil对象发消息而不会崩溃
3.如果以上都通过了,就通过isa指针开始查找这个类的方法列表,
先从缓存中找,完了跳到对应函数执行
4.如果缓存列表找不到,就找一下方法分发表
5.如果方法列表找不到,就到超类的方法分发列表找,
一直找到NSObject
6.如果还找不到,就开始进行动态方法解析,调用
+(Bool)resolveInstanceMethod(SEL)selector;(实例方法)
+(Bool)resolveClassMethod(SEL)selector;(类方法)
该方法的参数就是那个位置的选择子(方法),其返回值为bool,
表示这个类能否新增一个实例方法用于处理该未知的方法
一般用这个方法可以动态的添加方法
7.如果还是找不到,那么运行系统就会问,
能不能把这条消息转发给其他的接受者处理。调用
+(id)forwardingTargetForSelector(SEL)selector;
当前接收者如果能找到备选对象,那么将其返回,
如果找不到,返回nil
8.如果还是找不到,那么会创建一个NSInvocation对象,
把这个尚未处理的那条消息有关的全部细节都封装其中,
此对象包含选择子,目标对象及参数,
并调用下面的方法进行消息转发
-(void)forwardInvocation:(NSInvocation *)invocation;
9.如果最后都没有出来的话,那么就抛出异常
objc_msgSend
当前对象无论调用任何方法返回的都是当前对象
无论何时,要调用objc_msgSend函数,必须要将函数强制转
换成合适的函数指针类型才能调用。
其实编译器会根据情况在objc_msgSend,
objc_msgSend_stret, objc_msgSendSuper, 或
objc_msgSendSuper_stret四个方法中选择一个来调用。
消息转发:
消息转发机流程
1、动态方法解析resolveInstanceMethod
2、备用接受者forwardingTargetForSelector
3、完整转发forwardInvocation
你可以实现forwardInvocation方法,将消息转发给另一个
对象,forwardInvocation方法是一个动态方法,在响应者无法
响应方法时,会调用forwardInvocation方法,可以重写这个方
法,实现消息转发。
Objc中向一个nill对象发消息,会发生什么?
如果self为nil,那么selector也会为空,直接返回,不会出现问题。
(返回值时对象,是标量,结构体)
如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0
地址返回,所以不会出现任何错误
如果返回值类型是对象,那么返还nil
如果回值类型是标量,那么是0,
如果回值类型是结构体为0,
其他为返回值是未定义的。
但对于[NSNull null]对象发送消息时,是会crash的,因为
NSNull类只有一个null方法。
unrecognized selector sent to instance的错误是怎么回事?
这是因为这个对象已经被释放了(引用计数为0了),那么这个时候再去调用方
法肯定是会Crash的,因为这个时候这个对象就是一个野指针(指向僵尸对象
(对象的引用计数为0,指针指向的内存已经不可用)的指针)了,安全的做法
是释放后将对象重新置为nil,使它成为一个空指针,大家可以在关闭ARC后手
动release对象验证一下。
空指针和野指针的区别
没有存储任何内存地址的指针就称为空指针(NULL指针)
空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。
"野指针"不是NULL指针,是指向"垃圾"内存(不可用内存)的指针。
野指针是非常危险的。
函数调用和OC的消息传递机制区别
对于C语言,函数的调用在编译器的时候决定调用那个函数。编译完之后直接
顺序执行。
OC的函数调用成为消息发送。属于动态调用过程。在编译的时候决不能决定
真正调用那个函数(实时证明,在编译阶段,oc可以调用任何函数,及时这个
函数并未实现,只要申明过就不会报错,而c语言在编译阶段或报错)。
消息转发中的resolveInstanceMethod返回值是Bool的话,那不去动态增加方法,直接返回yes行不行?
返回值文档中说是表示动态决议成功与否。但在上面的例子中(不涉
及消息转发的情况下),如果在该函数内为指定的 selector 提供
实现,无论返回 YES 还是 NO,编译运行都是正确的;但如果在该
函数内并不真正为 selector 提供实现,无论返回 YES 还是
NO,运行都会 crash,道理很简单,selector 并没有对应的实
现,而又没有实现消息转发。
resolveInstanceMethod 是为对象方法进行决议,
而 resolveClassMethod 是为类方法进行决议。
NSInvocation
NSInvocation是一个消息调用类,它包含了所有OC消息的成分:
target、selector、参数以及返回值。NSInvocation可以将消
息转换成一个对象,消息的每一个参数能够直接设定,而且当一个
NSInvocation对象调度时返回值是可以自己设定的。一个
NSInvocation对象能够重复的调度不同的目标(target),而且
它的selector也能够设置为另外一个方法签名。NSInvocation
遵守NSCoding协议,但是仅支持NSPortCoder编码,不支持归档
型操作。
// 通过NSMethodSignature对象创建NSInvocation对象,NSMethodSignature为方法签名类
+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig;
// 获取NSMethodSignature对象
@property (readonly, retain) NSMethodSignature *methodSignature;
// 保留参数,它会将传入的所有参数以及target都retain一遍
- (void)retainArguments;
// 判断参数是否还存在
// 调用retainArguments之前,值为NO,调用之后值为YES
@property (readonly) BOOL argumentsRetained;
// 设置消息调用者,注意:target最好不要是局部变量
@property (nullable, assign) id target;
// 设置要调用的消息
@property SEL selector;
// 获取消息返回值
- (void)getReturnValue:(void *)retLoc;