iOS 利用runtime關聯對象


有次在大牛羣看到一個問題:如何給一個字典添加一個屬性(不能繼承),立馬蒙逼了,不能用繼承,難道用分類?但是分類貌似只能添加方法不能添加屬性啊,百思不得其解,直到後來接觸到了runtime才恍然大悟。


什麼是關聯對象

關聯對象是指某個OC對象通過一個唯一的key連接到一個類的實例上。

舉個例子:DuanPerson類的一個實例,他的狗dog(一個OC對象)通過一根繩子(key)被他牽着散步,這可以說Duannimo是關聯起來的,當然Duan可以牽着多個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:被關聯的對像(egDuan

const void *key:關聯的key,要求唯一

id value:關聯的對像(egdog

objc_AssociationPolicy policy:內存管理的策略


objc_AssociationPolicy policyenum值有:

/**

 * Policies related to associative references.

 * These are options to objc_setAssociatedObject()

 */

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. */

};

當對象被釋放時,會根據這個策略來決定是否釋放關聯的對象,當策略是retain/copy時,會釋放(release)關聯的對象,當是assign,將不會釋放。

值得注意的是,我們不需要主動調用removeAssociated來解除關聯的對象,如果需要解除指定的對象,可以使用setAssociatedObject置nil來實現。


關聯對象的應用

1.添加公用屬性

這是最常用的一個模式,通常我們會在類的聲明裏面添加屬性,但是出於某些需求,我們需要在分類裏添加一個貨多個屬性的話,編譯器就會報錯,這個問題的解決方案就是實用runtime的關聯對象。

eg:我們需要自定義一個tabBar,並暴露公共的屬性和方法。


@interface UITabBarController (custom)


@property (nonatomic,strong) UIView *customTabBar;


@end


#import "UITabBarController+custom.h"

#import <objc/runtime.h>


@implementation UITabBarController (custom)


- (void)setCustomTabBar:(UITabBar *)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 doSomeThing];


2.添加私有成員變量

有時候,需要在分類中添加不想暴露在公共聲明的成員變量。

eg:給按鈕添加點擊事件的回調

@interface UIButton (CallBack)


- (instancetype)initWithFrame:(CGRect)frame callBack:(void (^)(UIButton *))callBlock;


@end


@interface UIButton()


@property (nonatomic,copy) void (^callbackBlock)(UIButton * button);


@end


@implementation UIButton (CallBack)


- (void)setCallbackBlock:(void (^)(UIButton *))callbackBlock {

    objc_setAssociatedObject(self, @selector(callbackBlock), callbackBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);

}


- (void (^)(UIButton *))callbackBlock {

    return objc_getAssociatedObject(self, @selector(callbackBlock));

}


- (instancetype)initWithFrame:(CGRect)frame callBack:(void (^)(UIButton *))callBlock {

    if (self == [super initWithFrame:frame]) {

        self.callbackBlock = callBlock;

        [self addTarget:self action:@selector(didClickAction:) forControlEvents:UIControlEventTouchUpInside];

    }

    return self;

}


- (void)didClickAction:(UIButton *)button

{

    self.callbackBlock(button);

}


3.關聯KVO觀察者

有時候我們在分類中使用KVO,推薦使用關聯的對象作爲觀察者,儘量避免對像觀察自身。

此應用模式就不再舉例了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章