iOS 學習 --- Categary中聲明屬性注意事項

前言 

我們在 iOS 開發中經常需要使用分類(Category),爲已經存在的類添加屬性的需求,但是使用 @property並不能在分類中正確創建實例變量和存取方法。

不過,通過 Objective-C 運行時中的關聯對象,也就是 Associated Object,我們可以實現上述需求。

@property聲明屬性

@interface MyViewController :UIViewController
//屬性變量
@property (nonatomic, strong) UIButton *myButton;

@end

在使用上面代碼時會做3件事:

  • 生成實例變量 _myButton;
  • 生成getter方法:-myButton
  • 生成setter方法:-setMyButton:
@implementation MyViewController
{
    UIButton *_myButton;
}

-(UIButton *)myButton{
    return _myButton;
}

-(void)setMyButton:(UIButton *)myButton{
    _myButton = myButton;
}

這些代碼都是編譯器爲我們生成的,雖然我們看不到它,但是它確實在這裏,不需要我們自己再寫這些代碼。

類目(Categary)中用@property聲明屬性

UIViewController+CommonMethod.h

#import <UIKit/UIKit.h>

@interface UIViewController (CommonMethod)

@property (nonatomic, strong) UIButton *LoginButton;

@end

UIViewController+CommonMethod.m 中會報下面警告

這裏的警告告訴我們,loginButton 屬性的存取方法需要自己手動去實現,或者使用 @dynamic 在運行時實現這些方法。

換句話說,分類中的 @property 並沒有爲我們生成實例變量以及存取方法,而需要我們手動實現。

實現getter 和setter方法:

#import "UIViewController+CommonMethod.h"
#import <objc/runtime.h>
@implementation UIViewController (CommonMethod)


-(UIButton *)loginButton{
    return objc_getAssociatedObject(self, _cmd);
}

-(void)setLoginButton:(UIButton *)loginButton{
    objc_setAssociatedObject(self, @selector(loginButton), loginButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


@end

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


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);


OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

 

注意:

這裏的 _cmd 代指當前方法的選擇子,也就是 @selector(loginButton)。  

以鍵值對形式添加關聯對象

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

objc_setAssociatedObject :把一個對象與另外一個對象進行關聯。該函數需要四個參數:源對象,關鍵字,關聯的對象和一個關聯策略。

  • OBJC_EXPORT:打包lib時,用來說明該函數是暴露給外界調用的。
  • id _Nonnull object:表示關聯者,是一個對象,變量名理所當然也是object。
  • const void * _Nonnull key:關鍵字是一個void類型的指針。每一個關聯的關鍵字必須是唯一的。通常都是會採用靜態變量來作爲關鍵字。
  • id _Nullable value:表示被關聯者,變量名是value,它要關聯到object上的。
  • objc_AssociationPolicy policy:關聯策略表明了相關的對象是通過賦值,保留引用還是複製的方式進行關聯的;還有這種關聯是原子的還是非原子的。這裏的關聯策略和聲明屬性時的很類似。這種關聯策略是通過使用預先定義好的常量來表示的。
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {

    OBJC_ASSOCIATION_ASSIGN = 0,          

    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 

    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  

    OBJC_ASSOCIATION_RETAIN = 01401,      

    OBJC_ASSOCIATION_COPY = 01403         
};

 

******注意*****

斷開關聯是使用objc_setAssociatedObject函數,傳入nil值即可。

使用函數objc_removeAssociatedObjects可以斷開所有關聯。

根據 key 獲取關聯對象

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);

  • id _Nonnull object:
  • const void * _Nonnull key:

 

移除所有關聯對象

OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

  • id _Nonnull object:

這個方法不推薦使用,因爲會遍歷所有的關聯對象,並且全部釋放,可能會造成別的模塊功能缺陷。

相關文章:

iOS學習 --- 成員變量,實例變量,屬性

iOS 成員變量,實例變量,屬性變量的區別,聯繫

iOS 關於Category

Objective-C Associated Objects 的實現原理

category中添加屬性的簡單方式

AssociatedObject關聯對象原理實現

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