美文网首页
基于runtime源码了解OC的对象模型

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

作者: openlab | 来源:发表于2016-04-13 18:34 被阅读85次

    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,或者没有找到,报错。

    相关文章

      网友评论

          本文标题:基于runtime源码了解OC的对象模型

          本文链接:https://www.haomeiwen.com/subject/qfpilttx.html