前言
我們在 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:
這個方法不推薦使用,因爲會遍歷所有的關聯對象,並且全部釋放,可能會造成別的模塊功能缺陷。
相關文章: