在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
第一章:熟悉
|
关联类型 | 等效的 @property 属性 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic, retain |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic, copy |
OBJC_ASSOCIATION_RETAIN | retain |
OBJC_ASSOCIATION_COPY | copy |
根据给定的键从某对象中获取对应的关联对象值。
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
移除指定对象的全部关联对象。
objc_removeAssociatedObjects(id _Nonnull object)
注:只有在其他方法都行不通时才考虑使用它。若是滥用,则很快就会令代码失控,使其难于调试。
objc_msgSend
的作用代码:
id returnValue = [someObject messageName: paramter];
代码说明:
someObject
接受者。messageName
选择子。选择子和参数合起来称为“消息”
底层C语言代码实现:
id returnValue = objc_msgSend(someObject, @selector(messageName:), paramter);
原型代码:
void objc_msgSend(id self, SEL cmd, ...)
这是个“参数个数可变函数”,能接受两个或者两个以上的参数。
参数说明:
self
接受者。cmd
选择子(方法的名字)。- 后续参数为消息中的那些参数,顺序不变。
具体实现:
注:OC
方法调用需要很多步骤,较为耗时。objc_msgSend
会将匹配结果缓存在“快速映射表”,每个类都有这样一块缓存。虽然还是不如“静态绑定的函数调用操作”那么迅速,但是也不会慢很多。
边界情况:
objc_msgSend_stret
待发送的消息要返回结构体,就交由此函数处理。objc_msgSend_fpret
待发送的消息要返回浮点数,就交由此函数处理。objc_msgSendSuper
要给超类发消息,就交由此函数处理。如:[super message:parameter]
。
Objective-C
对象的每个方法都可以看做简单的 C
函数,其原型如下:
<return_type> Class_selector(id self, SEL _cmd, ...)
这个原型和 objc_msgSend
函数很像,是为了利用 “尾调用优化” 技术。令 “跳至方法实现” 这一操作跟简单些。
使用范围:某函数的最后一项操作仅仅是调用另一个函数而不会将其返回值另作他用。
步骤:编译器会生成调转至另一函数所需的指令码,不会向调用堆栈中推人新的 “栈帧”。
不优化后果:
Objective-C
方法之前,都需要为调用 objc_msgSend
函数准备 “栈帧”,可以在 “栈踪迹” 中看到。消息转发:
第一阶段:动态方法解析: 征询接收者(所属的类),看其是否能动态添加方法,以处理当前这个 “未知的选择子”。
第二阶段:
1. 备援的接收者: 请接收者看看有没有其他对象(备援的接收者)能处理这条消息。
2. 完整的消息转发机制: 运行期系统会把与消息有关的全部细节都封装到NSInvocation
对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。
是否能新增一个实例方法来处理选择子,调用方法如下:
// 实例方法
+ (BOOL)resolveInstanceMethod:(SEL)selector
// 类方法
+ (BOOL)resolveClassMethod:(SEL)selector
使用前提:相关方法的实现代码已经写好,只等运行的时候动态插入到类里面。
动态添加方法函数如下:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
参数说明:
cls
添加方法的类name
被添加方法的名字imp
函数指针,指向待添加的方法(C语言实现)。type
待添加方法的 “类型编码” 。
是否有其他对象处理这条消息,调用方法如下:
- (id)forwardingTargetForSelector:(SEL)selector
若找到备援对象,该方法返回备援对象,反之,返回 nil
。
注意:我们无法操作经由这一步所转发的消息。
创建 NSInvocation
对象,此对象包含 选择子 、目标 及 参数。
消息派发调用方法如下:
- (void)forwardInvocation:(NSInvocation *)invocation
若发现某调用操作不应由本类处理,则向上寻找,直至 NSObject
。如果最后调用了 NSObject
的方法,那么该方法还会继续调用 doesNotRecognizeSelector:
以抛出异常,表明选择子最终未能得到处理。
消息转发全流程如下图:
接收者在每一步中均有机会处理消息。步骤越往后,处理消息的代价就越大。
消息转发代码:https://github.com/AlonerOwl/Runtime/tree/master/Runtime/MessageSend
函数指针(IMP):id (*IMP)(id, SEL, ...)
方法表:函数指针所组成的一个集合。
操作类的方法表:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
第12条已经说过了。func method_exchangeImplementations(_ m1: Method, _ m2: Method)
参数:两个待交换的方法实现Method class_getInstanceMethod(Class cls, SEL name)
这个方法可以为那些 “完全不知道具体实现” 的黑盒方法增加日志记录功能,这非常有助于程序调试。
若是滥用,反而会令代码变的不易读懂且难于维护。
method swizzling代码:https://github.com/AlonerOwl/Runtime/tree/master/Runtime/MethodSwizzling
请发表评论