iOS RunTime 机制浅析(二)
2015-04-05 09:51
267 查看
继续上一篇未写完的内容。
上一篇写到runtime通过与Foundation Framework interact中的消息转发机制。
在第一点中我们提到我们代码中调用函数在runtime sys的执行过程,那么如果没有寻找到指定的函数呢?
这时候Runtime sys 会启用NSObject中的
forwardInvocation:
函数,这个函数附带一个NSInvocation类型的参数。
想象一下,如果有一个
- (id)negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
但是如果想真正地让某个对象来执行此函数,那么只有通过继承实现过此函数的类了。
以上这些做法都有缺点,就是当要实现的函数多的时候,那么必然要实现多个函数体进行转发,或者继承多个类去执行,这一点在objc的单继承下是不可能的。而且当函数多起来,会有很多不确定因素,一旦其他类中修改了某个函数,那么当前类的依赖关系也就受到较大的影响甚至无法执行。
另一个解决办法就是通过以上提到过的forwardInvocation:来实现消息的转发。
我们已经知道在类中寻找不到某个函数时,runtime sys会执行上述函数,那么在类中我们可以覆盖此函数为:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
这样子如果最终执行了函数,那么返回值会返回到最初的sender。
forwordInvacation:函数可以作为一个消息转发中心,把消息打包转发到不同的对象上,或者转发到同个目标。
还可以通过重写它,那么函数调用找不到指定函数时就不会出错了。
既然forwordInvacation:函数有以上特性,那么我们可以用它来模仿多重继承,这样在实际应用时可以为对象减少某些责任。如官方文档中给出的例子,下图fig-2.1。
fig-2.1
结合runtime的消息机制我们也不难理解,在模范多重继承时是如何执行的。
不过消息转发和多重继承还是有一定区别的,多重继承是把消息分散到多个类去执行,而消息转发是把功能集中在一个类,通过转发进行。
另一方面的功能就是surrogate代理。当我们有一个类包含了很多数据,还有很多功能时,我们希望能给他减肥,这时可以利用消息转发的机制为它创建一个surrogate。当消息到来时,就会自动转发了,从而减轻了该类的责任。
固然消息转发有它的优势,但是系统对于继承和转发还是有较大的区别的,比如以下函数:
respondsToSelector:
isKindOfClass:只能在基础时返回真,通过消息转发不能返回真。
当然解决的办法还是有的,就是覆盖原来的方法,比如:
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}这样子就可以实现转发判断功能了。
需要注意的是,在消息转发是,不能重写被转发的方法,否则转发无法进行。
至此,runtime的第二点功能实现就讲到这里,更多不明白的请参照官方文档。
第三
第三点比较好理解,就是利用runtime提供的纯C语言函数来实现运行时的动态调用,这些函数存放在
这一点之前有个type Encoding专题,就是在调用这些纯C函数时难免与objc存在类型上的不兼容,是如何解决的。
由于篇幅较为零散,大家有兴趣自行查看。
runtime提供的这些动态实时查询函数类似于一下这些:
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
就是运行时去获取类的属性列表,属性名等等信息。
我们有必要知道这些属性在编译器中是如何表示的,如下图。
那么当我们通过以上函数去查询是,就会有一下的结果
以上表格由苹果官方文档提供。
由于函数较多,就不一一介绍,有兴趣查看官方文档https://developer.apple.com/library/ios/documentation/Cocoa/Reference/ObjCRuntimeRef/
runtime的第三方面就大概是这样,更多的是工具函数的意思。
通过三方面的介绍,对于runtime也有较大方面的理解了。
总而言之,runtime是objc特有的一种动态机制,其只要体现在三个方面,代码中的函数调用消息机制,基于Foundation Framework的函数调用及转发等机制,还有runtime提供的纯C方法库实时机制。runtime也需要编译器中的runtime system来支持。理解runtime可以让我们更灵活的编写代码,当然也要注意有关的注意事项。
以上包括第一篇均为本人参考官方文档所理解的内容,有错误的地方还望大家多多指教,谢谢!
官方介绍文档 https://developer.apple.com/library/ios/documentation/Cocoa/Reference/ObjCRuntimeRef/ https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048-CH1-SW1
上一篇写到runtime通过与Foundation Framework interact中的消息转发机制。
在第一点中我们提到我们代码中调用函数在runtime sys的执行过程,那么如果没有寻找到指定的函数呢?
这时候Runtime sys 会启用NSObject中的
forwardInvocation:
函数,这个函数附带一个NSInvocation类型的参数。
想象一下,如果有一个
negotiate
函数想通过某个obj来调用,如果在函数中没有真正实现,那么可以转发到其他对象去执行,比如:
- (id)negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
但是如果想真正地让某个对象来执行此函数,那么只有通过继承实现过此函数的类了。
以上这些做法都有缺点,就是当要实现的函数多的时候,那么必然要实现多个函数体进行转发,或者继承多个类去执行,这一点在objc的单继承下是不可能的。而且当函数多起来,会有很多不确定因素,一旦其他类中修改了某个函数,那么当前类的依赖关系也就受到较大的影响甚至无法执行。
另一个解决办法就是通过以上提到过的forwardInvocation:来实现消息的转发。
我们已经知道在类中寻找不到某个函数时,runtime sys会执行上述函数,那么在类中我们可以覆盖此函数为:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
这样子如果最终执行了函数,那么返回值会返回到最初的sender。
forwordInvacation:函数可以作为一个消息转发中心,把消息打包转发到不同的对象上,或者转发到同个目标。
还可以通过重写它,那么函数调用找不到指定函数时就不会出错了。
既然forwordInvacation:函数有以上特性,那么我们可以用它来模仿多重继承,这样在实际应用时可以为对象减少某些责任。如官方文档中给出的例子,下图fig-2.1。
fig-2.1
结合runtime的消息机制我们也不难理解,在模范多重继承时是如何执行的。
不过消息转发和多重继承还是有一定区别的,多重继承是把消息分散到多个类去执行,而消息转发是把功能集中在一个类,通过转发进行。
另一方面的功能就是surrogate代理。当我们有一个类包含了很多数据,还有很多功能时,我们希望能给他减肥,这时可以利用消息转发的机制为它创建一个surrogate。当消息到来时,就会自动转发了,从而减轻了该类的责任。
固然消息转发有它的优势,但是系统对于继承和转发还是有较大的区别的,比如以下函数:
respondsToSelector:
isKindOfClass:只能在基础时返回真,通过消息转发不能返回真。
当然解决的办法还是有的,就是覆盖原来的方法,比如:
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}这样子就可以实现转发判断功能了。
需要注意的是,在消息转发是,不能重写被转发的方法,否则转发无法进行。
至此,runtime的第二点功能实现就讲到这里,更多不明白的请参照官方文档。
第三
第三点比较好理解,就是利用runtime提供的纯C语言函数来实现运行时的动态调用,这些函数存放在
/usr/include/objc目录中。
这一点之前有个type Encoding专题,就是在调用这些纯C函数时难免与objc存在类型上的不兼容,是如何解决的。
由于篇幅较为零散,大家有兴趣自行查看。
runtime提供的这些动态实时查询函数类似于一下这些:
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
const char *property_getName(objc_property_t property) objc_property_t class_getProperty(Class cls, const char *name) objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty) const char *property_getAttributes(objc_property_t property)
就是运行时去获取类的属性列表,属性名等等信息。
我们有必要知道这些属性在编译器中是如何表示的,如下图。
Code | Meaning |
---|---|
R | The property is read-only (readonly). |
C | The property is a copy of the value last assigned (copy). |
& | The property is a reference to the value last assigned (retain). |
N | The property is non-atomic (nonatomic). |
G<name> | The property defines a custom getter selector name. The name follows the G(for example, GcustomGetter,). |
S<name> | The property defines a custom setter selector name. The name follows the S(for example, ScustomSetter:,). |
D | The property is dynamic (@dynamic). |
W | The property is a weak reference (__weak). |
P | The property is eligible for garbage collection. |
t<encoding> | Specifies the type using old-style encoding. |
Property declaration | Property description |
---|---|
@property char charDefault; | Tc,VcharDefault |
@property double doubleDefault; | Td,VdoubleDefault |
@property enum FooManChu enumDefault; | Ti,VenumDefault |
@property float floatDefault; | Tf,VfloatDefault |
@property int intDefault; | Ti,VintDefault |
@property long longDefault; | Tl,VlongDefault |
@property short shortDefault; | Ts,VshortDefault |
@property signed signedDefault; | Ti,VsignedDefault |
@property struct YorkshireTeaStruct structDefault; | T{YorkshireTeaStruct="pot"i"lady"c},VstructDefault |
@property YorkshireTeaStructType typedefDefault; | T{YorkshireTeaStruct="pot"i"lady"c},VtypedefDefault |
@property union MoneyUnion unionDefault; | T(MoneyUnion="alone"f"down"d),VunionDefault |
@property unsigned unsignedDefault; | TI,VunsignedDefault |
@property int (*functionPointerDefault)(char *); | T^?,VfunctionPointerDefault |
@property id idDefault; Note: the compiler warns: "no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed" | T@,VidDefault |
@property int *intPointer; | T^i,VintPointer |
@property void *voidPointerDefault; | T^v,VvoidPointerDefault |
@property int intSynthEquals; In the implementation block: @synthesize intSynthEquals=_intSynthEquals; | Ti,V_intSynthEquals |
@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; | Ti,GintGetFoo,SintSetFoo:,VintSetterGetter |
@property(readonly) int intReadonly; | Ti,R,VintReadonly |
@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; | Ti,R,GisIntReadOnlyGetter |
@property(readwrite) int intReadwrite; | Ti,VintReadwrite |
@property(assign) int intAssign; | Ti,VintAssign |
@property(retain) id idRetain; | T@,&,VidRetain |
@property(copy) id idCopy; | T@,C,VidCopy |
@property(nonatomic) int intNonatomic; | Ti,VintNonatomic |
@property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; | T@,R,C,VidReadonlyCopyNonatomic |
@property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; | T@,R,&,VidReadonlyRetainNonatomic |
由于函数较多,就不一一介绍,有兴趣查看官方文档https://developer.apple.com/library/ios/documentation/Cocoa/Reference/ObjCRuntimeRef/
runtime的第三方面就大概是这样,更多的是工具函数的意思。
通过三方面的介绍,对于runtime也有较大方面的理解了。
总而言之,runtime是objc特有的一种动态机制,其只要体现在三个方面,代码中的函数调用消息机制,基于Foundation Framework的函数调用及转发等机制,还有runtime提供的纯C方法库实时机制。runtime也需要编译器中的runtime system来支持。理解runtime可以让我们更灵活的编写代码,当然也要注意有关的注意事项。
以上包括第一篇均为本人参考官方文档所理解的内容,有错误的地方还望大家多多指教,谢谢!
官方介绍文档 https://developer.apple.com/library/ios/documentation/Cocoa/Reference/ObjCRuntimeRef/ https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048-CH1-SW1
相关文章推荐
- iOS RunTime 机制浅析(一)
- 关于 IOS code signe 和 Provisioning Files 机制 浅析
- iOS-浅谈runtime运行时机制-runtime简单使用(转)
- iOS-浅谈runtime运行时机制01-类与对象的内部结构
- iOS开发之深入探讨runtime机制03-runtime的方法与消息
- iOS开发之深入探讨runtime机制01-类与对象
- iOS中runtime运行机制解析
- [IOS 开发] iOS中的RunTime机制
- 比较iOS类别Category和扩展Extension以及runtime的associative(关联)机制(容我水一发)
- iOS开发之旅--揭秘Runtime机制
- iOS-浅谈runtime运行时机制01-类与对象的内部结构
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS-浅谈runtime运行时机制01-类与对象的内部结构
- [IOS 开发] runtime 运行时机制 完全解读
- iOS Runtime机制 - 类和对象(1)
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS开发之深入探讨runtime机制01-类与对象
- iOS-浅谈runtime运行时机制02
- iOS开发之深入探讨runtime机制01-类与对象
- iOS中runtime运行机制解析