美文网首页
Category 增加weak属性

Category 增加weak属性

作者: 骨古 | 来源:发表于2018-08-01 14:47 被阅读56次

    背景

    因为 OC 中 无法直接为类新增属性(继承、私有 extension 除外),那么通过 category 结合 object association 是常用的为一个类添加属性的手段。主要是用到 <objc/runtime.h> 的两个函数:

    OBJC_EXPORT void
    objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy)
    
    OBJC_EXPORT id _Nullable
    objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
        OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
    

    为一个新的对象新增属性

    #import <objc/runtime.h>
    @interface Person (XAssociate)
    
    @property (nonatomic, weak) id xProperty;
    
    @end
    
    @implementation Person (XAssociate)
    
    - (void)setXProperty:(id)xProperty {
        objc_setAssociatedObject(self, @selector(xProperty), xProperty, OBJC_ASSOCIATION_ASSIGN);
    }
    
    - (id)xProperty {
        return objc_getAssociatedObject(self, _cmd);
    }
    
    @end
    

    由于 objc association 提供的策略中,没有直接提供 weak 的策略,一般情况下折中使用 上文实例代码中的assign 的方式:

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                                *   The association is not made atomically. */
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                                *   The association is not made atomically. */
        OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                                *   The association is made atomically. */
        OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                                *   The association is made atomically. */
    };
    

    使用assign策略本质上是保存了对象的地址而不是真正的弱引用,在一些情定的情况下 属性对象释放时再调用方法会出现野指针异常

    解决方案

    • 使用 strong + WeakContainer 的方式,实现对属性对象的 weak 引用。 思路是
      声明一个 WeakContainer 类对真实的属性对象进行 weak 属性引用
    • 通过 OBJC_ASSOCIATION_RETAIN_NONATOMIC 策略对WeakContainer 进行 retain association
    • 这样在 get 关联属性对象时由于 WeakContainer 对真是属性对象的 weak 引用,会返回 nil 而不是野指针

    实现WeakContainer

    @interface XWeakObjectContainer : NSObject
    
    @property (nonatomic, readonly, weak) id weakObject;
    
    - (instancetype)initWithWeakObject:(id)object;
    
    @end
    
    @implementation XWeakObjectContainer
    
    - (instancetype)initWithWeakObject:(id)object {
        self = [super init];
        if (self) {
            _weakObject = object;
        }
        
        return self;
    }
    
    @end
    

    从而变相地实现 weak association 如下:

    @implementation NSObject (XAssociate)
    
    - (void)setXProperty:(id)xProperty {
        XWeakObjectContainer *container = [[XWeakObjectContainer alloc] initWithWeakObject:xProperty];
        objc_setAssociatedObject(self, @selector(xProperty), container, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (id)xProperty {
        XWeakObjectContainer *container = objc_getAssociatedObject(self, _cmd);
        return container.weakObject;
    }
    
    @end
    

    备注

    虽然 retain 了一个 WeakContainer,但是 WeakContainer 最终会随着属性的持有对象一起销毁,不存在泄露。

    相关文章

      网友评论

          本文标题:Category 增加weak属性

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