博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
objc语言的运行时处理
阅读量:5234 次
发布时间:2019-06-14

本文共 4078 字,大约阅读时间需要 13 分钟。

在Objective-C中,消息是通过objc_msgSend()这个runtime方法及相近的方法来实现的。这个方法需要一个target,selector,还有一些参数。理论上来说,编译器只是把消息分发变成objc_msgSend来执行。比如下面这两行代码是等价的。

1 [array insertObject:foo atIndex:5];  2 objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

class的方法列表其实是一个字典,key为selectors,IMPs为value。一个IMP是指向方法在内存中的实现。很重要的一点是,selector和IMP之间的关系是在运行时才决定的,而不是编译时。这样我们就能玩出些花样。

IMP通常是指向方法的指针,第一个参数是self,类型为id,第二个参数是_cmd,类型为SEL,余下的是方法的参数。这也是self_cmd被定义的地方。下面演示了Method和IMP

1 - (id)doSomethingWithInt:(int)aInt{} 2 id doSomethingWithInt(id self, SEL _cmd, int aInt){}

 

objc中存在一些用于修改和自省的方法,这些方法差不多都是以特定的前缀开头,如

class_addIvar, class_addMethod,class_addProperty和class_addProtocol

允许重建classes,

class_copyIvarList,class_copyMethodList, class_copyProtocolList和class_copyPropertyList

能拿到一个class的所有内容,而

class_getClassMethod, class_getClassVariable, class_getInstanceMethod,class_getInstanceVariable, class_getMethodImplementation和class_getProperty

返回单个内容。也有些用于自省的方法,如

class_conformsToProtocol, class_respondsToSelector,class_getSuperclass。

最后,你可以使用class_createInstance来创建一个object。

比较基础的一个动态特性是通过String来生成Classes和Selectors。Cocoa提供了NSClassFromStringNSSelectorFromString方法,使用起来很简单

1 Class stringclass = NSClassFromString(@"NSString");2 NSString *myString = [stringclass stringWithString:@"Hello World"];

为什么要这么做呢,直接使用class不是更方便,通常情况下,但有些场景有会很能有用,首先可以得知某个class是否存在,因为不存在的话NSClassFromString会返回nil,用于检查。另一个常见的场景是根据不同的输入返回不同的class跟method。在解析数据的时候,下面是一个例子

- (void)parseObject:(id)object {    for (id data in object) {        if ([[data type] isEqualToString:@"String"]) {            [self parseString:[data value]];         } else if ([[data type] isEqualToString:@"Number"]) {            [self parseNumber:[data value]];        } else if ([[data type] isEqualToString:@"Array"]) {            [self parseArray:[data value]];        }    }}- (void)parseObjectDynamic:(id)object {    for (id data in object) {        [self performSelector:NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [data type]]) withObject:[data value]];    }}- (void)parseString:(NSString *)aString {}- (void)parseNumber:(NSString *)aNumber {}- (void)parseArray:(NSString *)aArray {}

可以看到用了后者的话可以把代码行数降下来,将来如果有新的类型,只需要增加实现方法即可,而不用去添加新的else if.

在objc中,方法由两部分组成,selector相当于一个方法的id,IMP是方法的实现,这样分开的一个便利之处就是selector和IMP之间的对应关系可以被改变。这就是Method Swizzling的存在处,交换两个方法的实现,下面是代码实现:

1 void MethodSwizzle(Class aClass, SEL orig_sel, SEL alt_sel){ 2     Method orig_method = nil, alt_method = nil; 3     // First, look for the methods 4     orig_method = class_getInstanceMethod(aClass, orig_sel); 5     alt_method = class_getInstanceMethod(aClass, alt_sel); 6     // If both are found, swizzle them 7     if ((orig_method != nil) && (alt_method != nil)){ 8         char *temp1; 9         IMP temp2;10         temp1 = orig_method->method_types;11         orig_method->method_types = alt_method->method_types;12         alt_method->method_types = temp1;13         temp2 = orig_method->method_imp;14         orig_method->method_imp = alt_method->method_imp;15         alt_method->method_imp = temp2;16     } 17 }

当然,上面的第八行开始到十五行之间的代码可以用如下进行替换。

method_exchangeImplementations(orig_method,alt_method)

上面我们谈到了方法交换,但是当你发送了一个object无法处理的消息时会发生什么呢?这里首先会是动态方法处理

1 resolveInstanceMethod && resolveClassMethod

在这两个重写的地方运用class_addMethod,同时记得返回YES,下面是一个例子

1 + (BOOL)resolveInstanceMethod:(SEL)aSelector {2     if (aSelector == @selector(myDynamicMethod)) {3         class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:");4         return YES;5     }6     return [super resolveInstanceMethod:aSelector];7 }

如果resolve method返回了NO,那么运行时就进入下一个步骤--消息转发。首先会调用-forwardingTargetForSelector:,如果只是把消息发送到另一个object,那么就用这个方法,但是如果你想修改消息,那么就要使用-forwardInvocation:,将消息打包成NSInvocation,调用invokeWithTarget:

整个文章下来,可以看到objc表面看起来跟c#,java等语言在方法调用上没什么区别,但最关键的是objc的运行时消息处理,我们可以在消息处理上添加更多的自由,其优势在于在不扩展语言本身的情况下做很多事,比如KVO,提供了优雅的API来与已有的代码进行无疑结合。

下面就结合运行时来谈谈KVO的内部真正实现。当你第一次观察某个object时,runtime会创建一个新的继承原先class的subclass。在这个新的class中,它重写了所有被观察的key,然后将object的isa指针指向新创建的class(这个指针告诉Objective-C运行时某个object到底是哪种类型的object)。所以object神奇地变成了新的子类的实例。

 

 

转载于:https://www.cnblogs.com/xwang/p/3542515.html

你可能感兴趣的文章
mysql安装linux_二进制包安装
查看>>
POJ 3280 Cheapest Palindrome
查看>>
POJ 2481-Cows(BIT)
查看>>
JavaScript模块化编程 - CommonJS, AMD ,CMD和 RequireJS之间的关系
查看>>
json字符串转为php数组,使用随机字符串生成订单号(学生笔记)
查看>>
current account(经常账户)
查看>>
Sharepoint 2010 备份与恢复 (一)
查看>>
前端布局模板
查看>>
keepalived+lvs子网掩码造成VIP切换故障 + vrrp_script+track_script
查看>>
python yield深入理解
查看>>
[每天进步一点 -- 流水账]第4周
查看>>
实验十三
查看>>
c++作业4 9.22
查看>>
How to Improve Reading Skills
查看>>
关于MySQL去除查询结果重复值
查看>>
当后台给的数据格式没有规律的时候,前端自我处理方式
查看>>
[转]SpringMVC拦截器详解[附带源码分析]
查看>>
题解 CF1119A 【Ilya and a Colorful Walk】
查看>>
如何做好产品
查看>>
JS windows对象的top属性
查看>>