iOS 用runtime為分類新增成員變數或屬性

盧三發表於2017-12-20

關於iOS分類:很多說法是隻能新增方法,而不能新增成員變數或屬性。有些人可能知道,這種說法是不嚴謹的,並不是絕對不能新增變數。 解釋如下: 我們知道在一個類中用@property宣告屬性,編譯器會自動幫我們生成_成員變數和setter/getter,但分類的指標結構體中,根本沒有屬性列表。所以在分類中用@property宣告屬性,既無法生成_成員變數也無法生成setter/getter。 因此結論是:我們可以用@property宣告屬性,編譯會通過,但run之後就會崩潰。

那麼問題來了。。 既然報錯的根本原因是使用了系統沒有生成的setter/getter方法,可不可以在手動新增setter/getter來避免崩潰,完成呼叫呢?其實是可以的。由於OC是動態語言,方法真正的實現是通過runtime完成的,雖然系統不給我們生成setter/getter,但我們可以通過runtime手動新增setter/getter方法。那具體怎麼實現呢?

舉例如下: 分類.h

@interface CustomView (dd)

- (NSString *)name;
- (void)setName:(NSString *)name;

@end
複製程式碼

分類.m

@implementation CustomView (dd)

/*
 * 使用關聯物件模擬例項變數
 * 使用objc_getAssociatedObject、objc_setAssociatedObject模擬『屬性』的存取方法
 */

- (NSString *)name{
    
    return objc_getAssociatedObject(self, _cmd);
    
}

- (void)setName:(NSString *)name{
    
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}
@end
複製程式碼

重點就是兩個runtime實現方法,如果不寫這個,只要你申明瞭屬性,執行肯定會報錯;如果加了這兩個方法,就能正常使用。 關聯策略的解釋如下:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy){

    OBJC_ASSOCIATION_ASSIGN = 0, //關聯物件的屬性是弱引用 

    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //關聯物件的屬性是強引用並且關聯物件不使用原子性

    OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //關聯物件的屬性是copy並且關聯物件不使用原子性

    OBJC_ASSOCIATION_RETAIN = 01401, //關聯物件的屬性是copy並且關聯物件使用原子性

    OBJC_ASSOCIATION_COPY = 01403 //關聯物件的屬性是copy並且關聯物件使用原子性
};
複製程式碼

相關文章