1. 屬性聲明的初現版本
2. 屬性聲明的概念
3. 屬性聲明的構成
4. 屬性訪問方式
複製代碼
1. @synthesize
2. @dynamic
3. 自動合成?
複製代碼
1. 方法名類關鍵字解析
2. 讀寫權限類關鍵字解析
3. 賦值操做類關鍵字解析 【重點】
4. 原子性操做類關鍵字解析
5. 類屬性關鍵字解析【 OC 新增】
複製代碼
1. 屬性在Runtime 的內部實現
2. protocol & category 的應用(關聯對象)
複製代碼
種類 | 關鍵字 | 描述 |
---|---|---|
修改方法名類 | setter = 新的 OC 方法名 | 修改默認生成的方法名( selector ) |
—— | getter = 新的 OC 方法名 | —— |
讀寫權限類 | readonly | 代表變量只讀,只生成 getter 方法 |
—— | readwrite | 代表變量可讀寫【默認值】 |
賦值操做類 | assign | 直接賦值 ( MRC / ARC 都可用 )【默認值】 |
—— | retain | 進行保持操做,持有對象 ( 僅 MRC 可用 ) |
—— | unsafe_unretained | 直接賦值 ( 僅 ARC ) |
—— | strong | 強引用,持有對象 ( 僅 ARC 可用 )【默認值】 |
—— | weak | 弱引用,不持有對象 ( 僅 ARC 可用 ) |
—— | copy | 拷貝副本 (深度拷貝) |
原子性操做類 | nonatomic | 非原子性操做,線程不安全 |
—— | atomic | 原子性操做,線程安全【默認值】 |
類屬性 | class | 永遠不自動合成存取方法,需手動實現;不聲明實例變量,由於它是類變量;【iOS 10, Xcode 8】 |
空類 | nonnull | 不能爲空【iOS 9, Xcode 7】 |
—— | nullable | 能夠爲空【iOS 9, Xcode 7】 |
—— | null_resettable | setter 方法能夠是 nil,getter 方法不能返回 nil,要重寫 getter 方法【iOS 9, Xcode 7】 |
—— | null_unspecified(_Null_unspecified) | 不肯定是否爲空【iOS 10, Xcode 8】(【iOS 9, Xcode 7】) |
詳細描述請移步至,本文 第三章: 屬性聲明的可選選項 ( 關鍵字 );html
注意:id 類型的變量不能使用點操做符進行訪問,緣由是 Xcode 不知道是否存在對應的存取方法;ios
@interface ......
@property (nonatomic, strong) int age;
@end
@implementaion XXXClass
@synthesize age = _age;
@end
複製代碼
// 狀況 1 readwrite,同時生成 setter、getter 方法
@property (nonatomic, strong) int age;
//////
- (void)setAge:(int)age {
// do something
}
- (int)age {
// do something
return _age;
}
//////
複製代碼
// 狀況 2 readonly,只生成 getter 方法
@property (nonatomic, strong, readonly) int age;
//////
- (int)age {
// do something
return _age;
}
//////
複製代碼
若是聲明瞭屬性 @property,同時又手動實現了相應的存取方法,就必定要寫 @synthesize 否則照樣報警告;程序員
@interface ......
@property (nonatomic, strong) int age;
@end
@implementaion XXXClass
@synthesize age = _age;
@end
複製代碼
告訴編譯器要手動編寫 setter、getter 方法(readwrite、readonly)編程
// 狀況 1 readwrite,必需要同時編寫 setter、getter 方法
@property (nonatomic, strong) int age;
//////
- (void)setAge:(int)age {
// do something
}
- (int)age {
// do something
return _age;
}
//////
複製代碼
// 狀況 2 readonly,只需編寫 getter 方法
@property (nonatomic, strong, readonly) int age;
//////
- (int)age {
// do something
return _age;
}
//////
複製代碼
此處程序員手動編寫的 setter 、getter 方法必需要嚴格按照 存取方法的命名要求進行編寫:數組
setter --> setValueName:
getter --> valueName
複製代碼
否則在調用屬性存取方法的時候,會出現訪問出錯的;安全
從 Xcode 4.4 開始,當咱們用 @property 進行屬性聲明的時候,編譯器就會自動幫咱們生成相應的 實例變量 + 存取方法聲明 + 存取方法實現;
那什麼狀況下會破壞這種自動合成的過程呢?
正常的使用狀況:bash
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic) NSUInteger age;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_age = 18;
self.age = 20;
NSLog(@"self.age = %lu", (unsigned long)self.age);
}
@end
複製代碼
實例變量 + 存取方法聲明 + 存取方法實現 都正常地擁有了;app
這裏直接證實瞭如下幾點:ide
** 讓警告消失 ** ui
#import "ViewController.h"
@interface ViewController (){
NSUInteger __age;
}
@property (nonatomic) NSUInteger _age;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_age = 18;
__age = 19;
self.age = 20;
self._age = 23;
NSLog(@"self.age = %lu", (unsigned long)self.age);
NSLog(@"self.age = %lu", (unsigned long)self._age);
}
@end
複製代碼
那個警告明顯是說,我自動合成的實例變量是__age
,而不是 _age
,因此你應該定義一個 __age
的實例變量纔對,否則我就警告你;
其實這裏是間接地證實了,若是你本身定義了相應的帶下劃線的實例變量,那麼 Xcode 就不會本身合成屬性相應的實例變量了;
簡而言之,寫了 NSUInteger __age;
和 @property (nonatomic) NSUInteger _age;
Xcode 只會合成相應的 存取方法聲明 + 存取方法實現;
_age
實例變量都不會幫你生成,固然連 age
實例變量也不會有;讓錯誤消失
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic) NSUInteger age;
@end
@implementation ViewController
@synthesize age = _age;
- (void)setAge:(NSUInteger)age {
_age = age;
}
- (NSUInteger)age {
return _age;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
age = 19;
_age = 18;
self.age = 20;
NSLog(@"self.age = %lu", (unsigned long)self.age);
}
@end
複製代碼
添加 @synthesize age = _age;
代碼就是告訴 Xcode 幫我把 age
改爲 _age
並生成相應的實例變量,屬性的錯誤就能夠修復了;
固然
age
那個錯誤能夠直接忽略,由於壓根就不會有它的出現;
固然若是隻手動作一個方法的實現:
由於這裏 age 默認是 readwrite 的,因此確定還有兩個方法(存取),若是隻手動實現其中一個,就至關於告訴 Xcode 我還有一個方法你幫我實現了吧;那麼若是屬性是 readonly 的呢?
若是是 readonly 的屬性聲明,只能夠有讀取方法(getter),因此你手動實現了它的 getter 方法,其實和 readwrite 狀況下手動實現 setter 和 getter 的狀況是同樣同樣的;讓錯誤消失
一樣地,添加@synthesize age = _age;
便可;
固然,它是沒有 setter 方法的,你也想要有,也能夠任性地本身寫一個,可是 readonly 爲何不改爲 readwrite 呢?
若是加個 @dynamic age;
呢?運行時掛 了:
不能夠掛
前者 setter Xcode 自動合成了,然後者是沒有合成,如今應該知道 @dynamic
的用意了吧。
@property ( setter = Age: ) int age;
其中**Age:
就是新的方法名,它實際上是一個 selector ;
它會替換默認生成的setAge:
**方法;
固然 getter 也是這樣使用;
readonly,只讀只生成相應的 getter 方法,以及帶下劃線的實例變量; @property ( readonly ) int age;
readwrite,生成 setter 、getter 方法,以及帶下劃線的實例變量; @property ( readwrite ) int age; -- a
@property int age; -- b
a、b 結果是同樣的,緣由是 readwrite 是默認的讀寫權限; 它們都生成了,setAge: 、age 存取方法的聲明和實現,_age 實例變量;
- assign、unsafe_unretained,僅用於基礎數據類型( C 類型)
- retain、strong、weak、copy,僅用於 OC 對象
// 屬性聲明
@property ( nonatomic, assign) int age;
// MRC 環境下,它們相同 @property ( nonatomic ) int age;
// setter 的自動實現
- (void)setAge:(int)age {
_age = age;
}
// getter 的自動實現
- (int)age {
return _age;
}
複製代碼
// 屬性聲明
@property ( nonatomic, unsafe_unretained ) int age;
// setter 的自動實現
- (void)setAge:(int)age {
_age = age;
}
// getter 的自動實現
- (int)age {
return _age;
}
複製代碼
// 屬性聲明
@property ( nonatomic, retain) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
if ( _obj != obj ) {
[_obj release];
_obj = [obj retain];
}
}
// getter 的自動實現
- (NSObject *)obj{
NSObject *objTemp = [ [ _obj retain ] autorelease ];
return objTemp;
}
複製代碼
// 屬性聲明
@property ( nonatomic, strong) NSObject *obj;
// 相同, @property ( nonatomic ) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
_obj = obj; // 這裏默認有 __strong 修飾
// __strong _obj = obj;
}
// getter 的自動實現
- (NSObject *)obj{
return _obj ;
}
複製代碼
// 屬性聲明
@property ( nonatomic, weak) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
__weak _obj = obj;
}
// getter 的自動實現
- (NSObject *)obj{
return _obj ;
}
複製代碼
// MRC 下:
// 屬性聲明
@property ( nonatomic, copy ) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
if ( _obj != obj ) {
[_obj release];
_obj = [obj copy];
}
}
// getter 的自動實現
- (NSObject *)obj{
NSObject *objTemp = [ [ _obj retain ] autorelease ];
return objTemp;
}
複製代碼
// ARC 下:
// 屬性聲明
@property ( nonatomic, copy ) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
_obj = [obj copy];
}
// getter 的自動實現
- (NSObject *)obj{
return _obj ;
}
複製代碼
注意:
@property ( nonatomic, copy ) NSMutableArray *mArray;
這種聲明真的是本身挖坑了;你要的是可變對象,可是一 copy 就是不可變對象了,運行時會出問題的;
// MRC 下:
// 屬性聲明
@property ( nonatomic, copy ) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
[_ex lock];
if ( _obj != obj ) {
[_obj release];
_obj = [obj copy];
}
[_ex unlock];
}
// getter 的自動實現
- (NSObject *)obj{
[_ex lock];
NSObject *objTemp = [ [ _obj retain ] autorelease ];
[_ex unlock];
return objTemp;
}
複製代碼
// ARC 下:
// 屬性聲明
@property ( nonatomic, copy ) NSObject *obj;
// setter 的自動實現
- (void)setObj:(NSObject *)obj{
[_ex lock];
_obj = [obj copy];
[_ex unlock];
}
// getter 的自動實現
- (NSObject *)obj{
return _obj ;
}
複製代碼
// 其實上面的例子已經不少了,因此這裏就不貼代碼了
複製代碼
@property ( nonatomic, class) int age;
@property ( nonatomic, class) NSObject *obj;
複製代碼
#import "ViewController.h"
@interface ViewController ()
@property (class, nonatomic) int number;
@end
複製代碼
@implementation ViewController
@dynamic number;
static int __number = -1;
- (void)setNumber:(int)n {
__number = n;
}
- (int)number {
return __number;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.number = 77;
NSLog(@"number %@", @(self.number));
}
@end
複製代碼
它們只能用於指針變量,固然實例對象確定是能夠用的咯
Xcode7 iOS9 OC新增 nonnull/nullable/null_resettable/null_unspecified
補充:若是聲明的屬性有多個是須要 nonnull 修飾的話,可使用一對宏來簡化屬性代碼:
// NS_ASSUME_NONNULL_BEGIN
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
// NS_ASSUME_NONNULL_END
#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
複製代碼
Ep:
#import "ViewController.h"
//
@interface ViewController ()
NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) NSUInteger *number;
@property (nonatomic) NSObject *obj;
@property (nonatomic) NSObject *obj1;
@property (nonatomic) NSObject *obj2;
@property (nonatomic) NSObject *obj3;
@property (nonatomic) NSObject *obj4;
@property (nonatomic) NSObject *obj5;
NS_ASSUME_NONNULL_END
@property (nonatomic) NSObject *obj6;
@end
//
@implementation ViewController
//
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
self.number = nil;
self.obj = nil;
self.obj6 = nil;
//
}
//
@end
複製代碼
nullable
setter、getter 方法不變;
null_resettable
setter 能夠是 nil,但 getter 不能返回nil;
重寫 setter 或 getter 方法,警告都會取消,可是正確的作法是重寫 getter 方法處理返回 nil 的狀況;
Ep:
#import "ViewController.h"
//
@interface ViewController ()
@property (nonatomic, null_resettable) NSString *obj;
@end
//
@implementation ViewController
//
//- (void)setObj:(NSString *)obj {
//
// if ([obj isEqualToString:@""]) {
// _obj = @"Hello !";
// }
//
//}
//
- (NSString *)obj {
//
if (_obj == nil) {
_obj = @"Hello !";
}
//
return _obj;
//
}
//
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
self.obj = nil;
//
}
//
@end
複製代碼
Ep【 Xcode 7, iOS 9】:
#import "ViewController.h"
//
@interface ViewController ()
@property (nonatomic) NSString * _Null_unspecified obj;
@end
//
@implementation ViewController
//
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
self.obj = nil;
//
}
//
@end
複製代碼
Ep【 Xcode 8, iOS 10】:
#import "ViewController.h"
//
@interface ViewController ()
@property (nonatomic, null_unspecified) NSString * obj;
@end
//
@implementation ViewController
//
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
self.obj = nil;
//
}
//
@end
複製代碼
補充:有一些關鍵字固然也能夠用於變量聲明那裏咯,大致有那些,你能夠試試;
#import "ViewController.h"
//
@interface ViewController ()
@property (nonatomic) NSString * const obj;
@property (nonatomic) __weak NSString * obj2;
@property (nonatomic) NSString * _Nonnull obj3;
@end
//
@implementation ViewController
//
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
self.obj = nil;
//
}
//
@end
複製代碼
核心內容在 <objc/runtime.h> 中:
核心參考《Objective-C Runtime Programming Guide》:typedef struct objc_property *Property;
複製代碼
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
複製代碼
objc_property 就是屬性的真正類型,是一個結構體,下面看看它的屬性描述定義:
/// Defines a property attribute
typedef struct {
const char *name; /**< The name of the attribute */
const char *value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
複製代碼
objc_property_attribute_t 是用於描述屬性的,就是存儲屬性的信息;
/**
* 類中聲明的全部屬性
*
* @param cls 你想檢查的類.
* @param outCount 存儲屬性的總數量
* 若是類中沒有聲明屬性,那麼 outCount 的值不會被改變
*
* @return objc_property_t * 數組
* 超類中的屬性聲明不會包含在裏面
* 終端會持續持有這些數組元素,因此不用的時候要用 free() 釋放掉
*
* 若是類中沒有聲明屬性或 cls = Nil ,那麼返回 NULL,且 outCount = 0
*/
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
下面這兩個是針對 protocol 的:
protocol_copyPropertyList --> 拷貝協議中聲明的全部屬性
/**
* 返回協議中聲明的全部實例屬性聲明
*
* @note 同於
* \code
* protocol_copyPropertyList2(proto, outCount, YES, YES);
* \endcode
*/
OBJC_EXPORT objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
protocol_copyPropertyList2 --> 功能同上,只不過它能夠區分類屬性和實例屬性
/**
* 返回協議中聲明的全部的屬性
*
* @param proto 協議
* @param outCount 存儲屬性聲明的總數
* @param isRequiredProperty \c YES 返回要求的屬性, \c NO 返回可選的屬性.
* @param isInstanceProperty \c YES 返回實例屬性, \c NO 返回類屬性.
*
* @return 是一個 C 類型的指針數組
* 其它採納了此協議的協議裏面的屬性聲明不會包含在這裏.
* 終端會持續持有這些數組元素,因此不用的時候要用 free() 釋放掉
* 若是類中沒有聲明屬性或 cls = Nil ,那麼返回 NULL,且 outCount = 0
*/
OBJC_EXPORT objc_property_t *protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount, BOOL isRequiredProperty, BOOL isInstanceProperty)
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
複製代碼
/**
* 根據提供的類和屬性名返回屬性
*
* @param cls 類
* @param name 屬性名
*
* @return objc_property_t * 指向的屬性
* 若是 cls = Nil 或者 沒有聲明相應的屬性,都會返回 NULL
*/
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
protocol_getProperty --> 獲取協議的某個屬性聲明
/**
* 根據 protocol 返回指定的屬性
*
* @param proto 協議
* @param name 屬性名
* @param isRequiredProperty \c YES 返回要求的屬性, \c NO 返回可選的屬性.
* @param isInstanceProperty \c YES 返回實例屬性, \c NO 返回類屬性.
*
* @return 根據參數列表的信息返回相應的屬性
* 若是協議沒有聲明相應的屬性會返回 NULL
*/
OBJC_EXPORT objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
屬性的相關方法
複製代碼
property_getName --> 獲取屬性聲明的屬性名
/**
* 返回屬性名
*
* @param property 屬性
*
* @return 是一個描述屬性名的 C 字符串
*/
OBJC_EXPORT const char *property_getName(objc_property_t property)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
property_getAttributes --> 獲取屬性聲明的屬性特徵
/**
* 返回屬性的特徵的字符串
*
* @param property 屬性
*
* @return 是一個描述屬性的特徵的 C 字符串
*
* @note 關於特徵字符串的格式在 《Objective-C Runtime Programming Guide》 Declared Properties 一節有描述
*/
OBJC_EXPORT const char *property_getAttributes(objc_property_t property)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
property_copyAttributeList --> 拷貝屬性聲明的全部屬性特徵
/**
* 返回屬性的特徵字符串數組
*
* @param property 要拷貝屬性特徵的屬性
* @param outCount 屬性特徵總數
*
* @return 屬性特徵的 C 數組,再也不使用的時候要使用 free() 釋放資源
*/
OBJC_EXPORT objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
複製代碼
property_copyAttributeValue --> 拷貝屬性聲明的全部屬性特徵值
/**
* 根據屬性的特徵名返回屬性特徵的值
*
* @param property 屬性
* @param attributeName C 字符串的屬性特徵名
*
* @return 返回 C 字符串形式的特徵值,若是 attributeName 沒有找到就會返回 nil;
*/
OBJC_EXPORT char *property_copyAttributeValue(objc_property_t property, const char *attributeName)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
複製代碼
實例變量的相關方法
複製代碼
ivar_getName --> 獲取實例變量的變量名
/**
* 返回實例變量的變量名
*
* @param v 實例變量
*
* @return C 字符串形式的實例變量的變量名
*/
OBJC_EXPORT const char *ivar_getName(Ivar v)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
ivar_getTypeEncoding --> 獲取實例變量的變量類型
/**
* 返回實例變量的變量類型
*
* @param v 實例變量
*
* @return C 字符串形式的實例變量的變量類型
*
* @note 對於變量的可用類型查看《 Objective-C Runtime Programming Guide 》 Type Encodings 一節
*/
OBJC_EXPORT const char *ivar_getTypeEncoding(Ivar v)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
ivar_getOffset --> 獲取實例變量的內存偏移量
/**
* 返回實例變量的內存偏移量
*
* @param v 實例變量
*
* @return 實例變量的內存偏移量
*
* @note 若是是對象類型的實例,就要使用 object_getIvar 來替換這個方法進行訪問內存偏移量
*/
OBJC_EXPORT ptrdiff_t ivar_getOffset(Ivar v)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
複製代碼
// 假設有一個類,類名爲 ViewController
//
{
Class cls = objc_getClass("ViewController");
int propertiesCount = -1;
objc_property_t *properties = class_copyPropertyList(&propertiesCount);
for (int i = 0, i < propertiesCount, i++) {
objc_property_t property = properties [i];
NSLog(@"i --> Name: %s, Attributes: %s", property_getName(property), property_getAttributes(property));
}
}
複製代碼
重複:若是聲明並實現了屬性的存取方法就等同於實現了屬性;
objc_setAssociatedObject --> 設置指定實例對象的關聯值
/**
* 根據實例對象(object)、key 、policy 關聯一個值
*
* @param object 要關聯的實例對象
* @param key 用於關聯的 key
* @param value 要關聯的值,傳入 nil 就至關於重置關聯值
* @param policy setter 方法的行爲,詳細的要查看《 Objective-C Runtime Programming Guide 》 「Associative Object Behaviors.」 一節
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
複製代碼
補充:Associative Object Behaviors 的內容 描述:就是一個枚舉類型,不過裏面的值比較特殊而已
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
複製代碼
OBJC_ASSOCIATION_ASSIGN --> assign / weak ,直接賦值; OBJC_ASSOCIATION_RETAIN_NONATOMIC --> retain / strong 持有對象,非原子性,線程不安全; OBJC_ASSOCIATION_COPY_NONATOMIC --> 拷貝不可變副本,非原子性,線程不安全; OBJC_ASSOCIATION_RETAIN --> retain / strong 持有對象,原子性,線程安全; OBJC_ASSOCIATION_COPY --> 拷貝不可變副本,原子性,線程安全;
objc_getAssociatedObject --> 獲取指定實例對象的關聯值
/**
* 根據實例對象和關聯的 key 返回相應的關聯值
*
* @param object 實例對象
* @param key 關聯的 key
*
* @return 指定實例對象的關聯值
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
複製代碼
objc_removeAssociatedObjects --> 移除實例對象的全部關聯值
/**
* 移除實例對象的全部關聯值
*
* @param object 實例對象
*
* @note 這個方法的核心目的是爲了方便讓實例對象的全部關聯值還原到初始狀態;你不該該使用此方法來對一個關聯值的進行還原,而應使用 objc_setAssociatedObject 寫入 nil 來達到此目的。
*
* @see objc_setAssociatedObject
* @see objc_getAssociatedObject
*/
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
複製代碼
Ep:
#import <UIKit/UIKit.h>
@interface UIViewController (Test)
- (void)setTestValue:(NSString *)test;
- (NSString *)testValue;
@end
複製代碼
#import "UIViewController+Test.h"
#import <objc/runtime.h>
static const void * TestValueKey = &TestValueKey;
@implementation UIViewController (Test)
- (void)setTestValue:(NSString *)test {
objc_setAssociatedObject(self, TestValueKey, test, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)testValue {
return objc_getAssociatedObject(self, TestValueKey);
}
@end
複製代碼
具體使用:
#import "ViewController.h"
#import "UIViewController+Test.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.testValue = @"Test Runtime Associated";
NSLog(@"testValue : %@", self.testValue);
}
@end
複製代碼
Cocoa沒有任何真正的私有方法。只要知道對象支持的某個方法的名稱,即便該對象所在的類的接口中沒有該方法的聲明,你也能夠調用該方法。不過這麼作編譯器會報錯,可是隻要新建一個該類的類別,在類別.h文件中寫上原始類該方法的聲明,類別.m文件中什麼也不寫,就能夠正常調用私有方法了。這就是傳說中的私有方法前向引用。 因此說cocoa沒有真正的私有方法。 —— 來自文章《類別(Category)的做用(二)---對私有方法的前向引用》
《 Objective-C 編程全解 》第3版
《Objective-C Runtime Programming Guide》
《runtime之玩轉成員變量》
《Objective-C Runtime 運行時之二:成員變量與屬性》
《Swift 3.0 使人興奮,但Objective-C也有小改進--Objective-C的類屬性》
《iOS9的幾個新關鍵字(nonnull、nullable、null_resettable、__null_unspecified)》
《Objective—C語言的新魅力——Nullability、泛型集合與類型延拓》