基于runtime源码了解OC的对象模型

2016-04-13  本文已影响85人  openlab

runtime源码下载地址
http://opensource.apple.com/tarballs/objc4/

运行时可以干嘛

参考文档

http://www.ianisme.com/ios/2019.html?utm_source=tuicool&utm_medium=referral

很清晰

代码参考

https://github.com/ianisme/IANRuntimeStudy

runtime的作用

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

Class stringclass = NSClassFromString(@"NSString");

于是我们就得到了一个string class。接下来:

  NSString *myString = [stringclass stringWithString:@"Hello World"];

为什么要这么做呢?直接使用Class不是更方便?通常情况下是,但有些场景下这个方法会很有用。首先,可以得知是否存在某个class,NSClassFromString
会返回nil,如果运行时不存在该class的话。

另一个使用场景是根据不同的输入返回不同的class或method。比如你在解析一些数据,每个数据项都有要解析的字符串以及自身的类型(String,Number,Array)。你可以在一个方法里搞定这些,也可以使用多个方法。其中一个方法是获取type,然后使用if来调用匹配的方法。另一种是根据type来生成一个selector,然后调用之。以下是两种实现方式:

- (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 {}

可一看到,你可以把7行带if的代码变成1行。将来如果有新的类型,只需增加实现方法即可,而不用再去添加新的 else if。
Method Swizzling

#import  <objc/runtime.h>

@interface NSMutableArray (LoggingAddObject)
- (void)logAddObject:(id)aObject;
@end

@implementation NSMutableArray (LoggingAddObject)

+ (void)load {
    Method addobject = class_getInstanceMethod(self, @selector(addObject:));
    Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:));
    method_exchangeImplementations(addObject, logAddObject);
}

- (void)logAddObject:(id)aobject {
    [self logAddObject:aObject];
    NSLog(@"Added object %@ to array %@", aObject, self);
}

@end

isa swizzling

https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html

KVO应用

object_setClass(myObject, [MySubclass class]);

这个设计特别方便 动态更改isa

消息转发

如果 resolve method 返回NO,运行时就进入下一步骤:
消息转发。有两种常见用例。

  1. 将消息转发到另一个可以处理该消息的object。

  2. 将多个消息转发到同一个方法。

消息转发分两步。

首先,运行时调用-forwardingTargetForSelector:,如果只是想把消息发送到另一个object,那么就使用这个方法,因为更高效。

如果想要修改消息,那么就要使用-forwardInvocation:,运行时将消息打包成NSInvocation,然后返回给你处理。处理完之后,调用invokeWithTarget:。

Cocoa有几处地方用到了消息转发,

主要的两个地方是代理(Proxies)和响应链(Responder Chain)。

NSProxy是一个轻量级的class,它的作用就是转发消息到另一个object。

如果想要惰性加载object的某个属性会很有用。NSUndoManager也有用到,不过是截取消息,之后再执行,而不是转发到其他的地方。

响应链是关于Cocoa如何处理与发送事件与行为到对应的对象。

比如说,使用Cmd+C执行了copy命令,会发送-copy:到响应链。

首先是First Responder,通常是当前的UI。

如果没有处理该消息,则转发到下一个-nextResponder。

这么一直下去直到找到能够处理该消息的object,或者没有找到,报错。

上一篇 下一篇

猜你喜欢

热点阅读