Category 增加weak属性

背景

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

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 
@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属性)