美文网首页
Runtime中Associated Objects应用

Runtime中Associated Objects应用

作者: 喜欢就可以 | 来源:发表于2016-02-24 12:35 被阅读29次

    Associated Objects

    Mattt Thompson撰写、 Croath Liu翻译、 发布于2014年2月10日

    import <objc/runtime.h>

    Objective-C开发者应该小心谨慎地遵循这个危险咒语的各种准则。一个很好的原因的就是:混乱的运行时代码会改变运行在其架构之上的所有代码。

    从利的角度来讲, <objc/runtime.h> 中的函数具有其他方式做不到的、能为应用和框架提供强大功能的能力。而从弊的角度来讲,它可能会会毁掉代码的sanity meter,一切代码和逻辑都可能被异常糟糕的副作用影响(terrifying side-effects)。

    因此,我们怀着巨大的恐惧来思考这个与“魔鬼的交易”(Faustian bargain),一起来看看这个最多地被NSHipster读者们要求讲讲的主题之一:对象关联(associated objects)。


    对象关联(或称为关联引用)本来是Objective-C 2.0运行时的一个特性,起始于OS X Snow Leopard和iOS 4。相关参考可以查看<objc/runtime.h> 中定义的以下三个允许你将任何键值在运行时关联到对象上的函数:

    • objc_setAssociatedObject
    • objc_getAssociatedObject
    • objc_removeAssociatedObjects

    为什么我说这个很有用呢?因为这允许开发者对已经存在的类在扩展中添加自定义的属性,这几乎弥补了Objective-C最大的缺点。

    
    NSObject+AssociatedObject.h
    
    Objective-C
    @interface NSObject (AssociatedObject)
    @property (nonatomic, strong) id associatedObject;
    @end
    
    
    NSObject+AssociatedObject.m
    
    Objective-C
    @implementation NSObject (AssociatedObject)
    @dynamic associatedObject;
    
    - (void)setAssociatedObject:(id)object {
         objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (id)associatedObject {
        return objc_getAssociatedObject(self, @selector(associatedObject));
    }
    
    

    通常推荐的做法是添加的属性最好是 static char 类型的,当然更推荐是指针型的。通常来说该属性应该是常量、唯一的、在适用范围内用getter和setter访问到:

    Objective-C
    static char kAssociatedObjectKey;
    
    objc_getAssociatedObject(self, &kAssociatedObjectKey);
    

    然而可以用更简单的方式实现:用selector。

    Since SELs are guaranteed to be unique and constant, you can use _cmd as the key > for objc_setAssociatedObject(). #objective-c #snowleopard

    — Bill Bumgarner (@bbum) August 28, 2009
    关联对象的行为

    <h2 id="关联对象的行为">关联对象的行为</h2>

    <p>属性可以根据定义在枚举类型 <code>objc_AssociationPolicy</code> 上的行为被关联在对象上:</p>

    Behavior @property Equivalent Description
    OBJC_ASSOCIATION_ASSIGN @property (assign) @property (unsafe_unretained) 指定一个关联对象的弱引用
    OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) 指定一个关联对象的强引用,不能被原子化使用。
    OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) 指定一个关联对象的copy引用,不能被原子化使用。
    OBJC_ASSOCIATION_RETAIN @property (atomic, strong) 指定一个关联对象的强引用,能被原子化使用
    OBJC_ASSOCIATION_COPY @property (atomic, copy) 指定一个关联对象的copy引用,能被原子化使用

    OBJC_ASSOCIATION_ASSIGN</code> 类型关联在对象上的弱引用不代表0 retian的 <code>weak</code> 弱引用,行为上更像 <code>unsafe_unretained</code> 属性,所以当在你的视线中调用weak的关联对象时要相当小心。</p>

    <blockquote>
    <p>根据<a href="https://developer.apple.com/videos/wwdc/2011/#322-video">WWDC 2011, Session 322</a> (第36分钟左右)发布的内存销毁时间表,被关联的对象在生命周期内要比对象本身释放的晚很多。它们会在被 <code>NSObject -dealloc</code> 调用的 <code>object_dispose()</code> 方法中释放。</p>
    </blockquote>

    <h2 id="删除属性">删除属性</h2>

    <p>你可以会在刚开始接触对象关联时想要尝试去调用 <code>objc_removeAssociatedObjects()</code> 来进行删除操作,但<a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/objc_removeAssociatedObjects">如文档中所述</a>,你不应该自己手动调用这个函数:</p>

    <blockquote>
    <p>此函数的主要目的是在“初试状态”时方便地返回一个对象。你不应该用这个函数来删除对象的属性,因为可能会导致其他客户对其添加的属性也被移除了。规范的方法是:调用 <code>objc_setAssociatedObject</code> 方法并传入一个 <code>nil</code> 值来清除一个关联。</p>
    </blockquote>

    <h2 id="优秀样例">优秀样例</h2>

    <ul>
    <li><strong>添加私有属性用于更好地去实现细节。</strong>当扩展一个内建类的行为时,保持附加属性的状态可能非常必要。注意以下说的是一种非常<em>教科书式</em>的关联对象的用例:AFNetworking在 <code>UIImageView</code> 的category上用了关联对象来<a href="https://github.com/AFNetworking/AFNetworking/blob/2.1.0/UIKit%2BAFNetworking/UIImageView%2BAFNetworking.m#L57-L63">保持一个operation对象</a>,用于从网络上某URL异步地获取一张图片。</li>
    <li><strong>添加public属性来增强category的功能。</strong>有些情况下这种(通过关联对象)让category行为更灵活的做法比在用一个带变量的方法来实现更有意义。在这些情况下,可以用关联对象实现一个一个对外开放的属性。回到上个AFNetworking的例子中的 <code>UIImageView</code> category,<a href="https://github.com/AFNetworking/AFNetworking/blob/2.1.0/UIKit%2BAFNetworking/UIImageView%2BAFNetworking.h#L60-L65">它的 <code>imageResponseSerializer</code></a>方法允许图片通过一个滤镜来显示、或在缓存到硬盘之前改变图片的内容。</li>
    <li><strong>创建一个用于KVO的关联观察者。</strong>当在一个category的实现中使用<a href="http://nshipster.com/key-value-observing/">KVO</a>时,建议用一个自定义的关联对象而不是该对象本身作观察者。ng an associated observer for KVO**. When using <a href="http://nshipster.com/key-value-observing/">KVO</a> in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself.</li>
    </ul>

    <h2 id="反例">反例</h2>

    <ul>
    <li><strong>当值不需要的时候建立一个关联对象。</strong>一个常见的例子就是在view上创建一个方便的方法去保存来自model的属性、值或者其他混合的数据。如果那个数据在之后根本用不到,那么这种方法虽然是没什么问题的,但用关联到对象的做法并不可取。</li>
    <li><strong>当一个值可以被其他值推算出时建立一个关联对象。</strong>例如:在调用 <code>cellForRowAtIndexPath:</code> 时存储一个指向view的 <code>UITableViewCell</code> 中accessory view的引用,用于在 <code>tableView:accessoryButtonTappedForRowWithIndexPath:</code> 中使用。</li>
    <li><strong>用关联对象替代X</strong>,这里的X可以代表下列含义:

    <ul>
    <li>当继承比扩展原有的类更方便时用<a href="https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html">子类化</a>。</li>
    <li>为事件的响应者添加<a href="https://developer.apple.com/library/ios/documentation/general/conceptual/Devpedia-CocoaApp/TargetAction.html">响应动作</a>。</li>
    <li>当响应动作不方便使用时使用的<a href="https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html">手势动作捕捉</a>。</li>
    <li>行为可以在其他对象中被代理实现时要用<a href="https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html">代理(delegate)</a>。</li>
    <li>用<a href="http://nshipster.com/nsnotification-and-nsnotificationcenter/">NSNotification 和 NSNotificationCenter</a>进行松耦合化的跨系统的事件通知。


    比起其他解决问题的方法,关联对象应该被视为最后的选择(事实上category也不应该作为首选方法)。
    和其他精巧的trick、hack、workaround一样,一般人都会在刚学习完之后乐于寻找场景去使用一下。尽你所能去理解和欣赏它在正确使用时它所发挥的作用,同时当你选择<em>这个</em>解决办法时,也要避免当被轻蔑地问起“这是个什么玩意?”时的尴尬。</p>

    相关文章

      网友评论

          本文标题:Runtime中Associated Objects应用

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