zoukankan      html  css  js  c++  java
  • iOS runtime实战应用:关联对象

    在开始之前建议先阅读iOS runtime的基础理解篇:iOS内功篇:runtime

    有筒子在面试的时候,遇到这样一个问题:“如何給NSArray添加一个属性(不能使用继承)”,筒子立马蒙逼了,不能用继承,难道用分类?但是分类貌似只能添加方法不能添加属性啊,筒子百思不得其解,直到后来接触到了runtime才恍然大悟。

    什么是关联对象

    关联对象是指某个OC对象通过一个唯一的key连接到一个类的实例上。
    举个例子:xiaoming是Person类的一个实例,他的dog(一个OC对象)通过一根绳子(key)被他牵着散步,这可以说xiaoming和dog是关联起来的,当然xiaoming可以牵着多个dog。

    怎样关联对象

    runtime提供給我们的方法:

    //关联对象
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    //获取关联的对象
    id objc_getAssociatedObject(id object, const void *key)
    //移除关联的对象
    void objc_removeAssociatedObjects(id object)

    变量说明:

    id object:被关联的对象(如xiaoming)
    const void *key:关联的key,要求唯一
    id value:关联的对象(如dog)
    objc_AssociationPolicy policy:内存管理的策略

    objc_AssociationPolicy policy的enum值有:

    OBJC_ASSOCIATION_ASSIGN = 0,          
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
    OBJC_ASSOCIATION_RETAIN = 01401,       
    OBJC_ASSOCIATION_COPY = 01403

    当对象被释放时,会根据这个策略来决定是否释放关联的对象,当策略是RETAIN/COPY时,会释放(release)关联的对象,当是ASSIGN,将不会释放。
    值得注意的是,我们不需要主动调用removeAssociated来接触关联的对象,如果需要解除指定的对象,可以使用setAssociatedObject置nil来实现。

    关联对象的应用

    1、添加公共属性

    这是最常用的一个模式,通常我们会在类声明里面添加属性,但是出于某些需求(如前言描述的情况),我们需要在分类里添加一个或多个属性的话,编译器就会报错,这个问题的解决方案就是使用runtime的关联对象。
    应用举例:
    我们需要自定义一个tabbar,并暴露公共的属性和方法。(读者们可以思考下使用继承和分类实现的优点和不足之处)

    @interface UITabBarController (Custom)
    
    @property (nonatomic, strong) SUCustomTabbar * customTabbar;
    
    @end
    #import "UITabBarController+Custom.h"
    #import <objc/runtime.h>
    
    @implementation UITabBarController (Custom)
    
    - (void)setCustomTabbar:(UIView *)customTabbar {
        //这里使用方法的指针地址作为唯一的key
        objc_setAssociatedObject(self, @selector(customTabbar), customTabbar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (UIView *)customTabbar {
        return objc_getAssociatedObject(self, @selector(customTabbar));
    }
    
    //其他方法...
    
    @end

    这样,我们就可以像原生的tabbar一样使用自定义的tabbar:

    [self.tabBarController.customTabbar doSomgthig];

    2、添加私有成员变量

    有时候,需要在分类中添加不想暴露在公共声明的成员变量。
    应用举例:給按钮添加点击时间的回调

    @interface UIButton (Callback)
    
    - (instancetype)initWithFrame:(CGRect)frame callback:(void (^)(UIButton *))callbackBlock;
    
    @end
    @interface UIButton ()
    
    @property (nonatomic, copy) void (^callbackBlock)(UIButton * button);
    
    @end
    
    @implementation UIButton (Callback)
    
    - (void (^)(UIButton *))callbackBlock {
        return objc_getAssociatedObject(self, @selector(callbackBlock));
    }
    
    - (void)setCallbackBlock:(void (^)(UIButton *))callbackBlock {
        objc_setAssociatedObject(self, @selector(callbackBlock), callbackBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    - (instancetype)initWithFrame:(CGRect)frame callback:(void (^)(UIButton *))callbackBlock {
    
        if (self = [super initWithFrame:frame]) {
            self.callbackBlock = callbackBlock;
            [self addTarget:self action:@selector(didClickAction:) forControlEvents:UIControlEventTouchUpInside];
        }
        return self;
    }
    
    - (void)didClickAction:(UIButton *)button {
        self.callbackBlock(button);
    }
    
    @end

    读者思考:以上代码是否会存在内存管理的问题

    3、关联KVO观察者
    有时候我们在分类中使用KVO,推荐使用关联的对象作为观察者,尽量避免对象观察自身。
    此应用模式不再举例,有兴趣的读者可以自行深入研究,或者将代码贴到评论处。

    思考

    1、关联对象更多的应用模式?
    2、关联对象是否是解决问题的首选?
    3、关联对象的副作用?

    欢迎大家交流探讨。

    转自:http://www.jianshu.com/p/c68cc81ef763

  • 相关阅读:
    LeetCode 453 Minimum Moves to Equal Array Elements
    LeetCode 112 Path Sum
    LeetCode 437 Path Sum III
    LeetCode 263 Ugly Number
    Solutions and Summay for Linked List Naive and Easy Questions
    AWS–Sysops notes
    Linked List
    All About Linked List
    datatable fix error–Invalid JSON response
    [转]反编译c#的相关问题
  • 原文地址:https://www.cnblogs.com/dannygao/p/7374852.html
Copyright © 2011-2022 走看看