您的位置:首页 > 移动开发 > IOS开发

iOS RunTime 机制浅析(二)

2015-04-05 09:51 267 查看
继续上一篇未写完的内容。

上一篇写到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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: