0x01 屬性聲明文法
通過在@property後的括号内添加屬性特質參數,可以影響存取方法的生成。
聲明property的文法為:@property (參數1,參數2,...) 類型 名字
@interface Test : NSObject
// 括号内添加屬性特質進行限制
@property(nonatomic, readwrite, copy) NSString *name;
@end
0x02 屬性參數
屬性參數的分類
屬性參數主要可以分為三類:
- 讀寫語義:readwrite,readonly,getter,setter
- 記憶體管理語義:assign,retain,copy,unsafe_unretained
- 擁有者屬性:weak,strong
- 原子性: atomic,nonatomic
屬性參數的語義
讀寫語義
- readwrite:預設的屬性,變量可讀可寫,編譯器自動生成setter / getter方法;
- readonly:變量隻讀,隻生成getter方法沒有setter方法。但是我們可以自己補上setter方法來修改變量。
以下是readonly屬性驗證程式:
#import <Foundation/Foundation.h>
#import "Tire.h"
@interface AllWeatherRadial : Tire
@property (readonly) float rainHandling;
-(float)setRainHandling:(float)rainHandling; //手動補充setter方法聲明
//由于是readonly屬性是以必須先在接口中聲明
//否則雖然編譯時不會報錯
//但程式運作過程中如果對rainHandling指派,會因為找不到setter方法而崩潰
@property float snowHandling; //snowHandling預設是readwrite屬性
@end // AllWeatherRadial.h
//----------------------------------------------------------------------------
//AllWeatherRadial.m
#import "AllWeatherRadial.h"
@implementation AllWeatherRadial
@synthesize rainHandling = _rainHandling; //Xcode 4.5後的編譯器并不需要這行代碼
-(float)setRainHandling: (float)rh
{
_rainHandling = rh; //注意這裡必須使用變量名_rainHandling,記住rainHandling是屬性名
} //rainHandling的setter方法實作
...
//main.m檔案中的代碼不需要修改
readwrite 和 readonly是兩個互斥屬性。
- getter = < gettername >, setter = < settername >: 可以選擇性的在括号裡直接指定存取方法的方法名,但有可能會破壞健/值規則。是以如非必要,盡量避免使用。在此不展開詳述。
記憶體管理語義
- assign:預設屬性類型,是最簡單的指派特性,它不會對索引計數(Reference Counting)進行更改。變量的setter方法直接指派,而不進行retain操作:
@property NSString *str;
//-----------------------------------------------
-(void)setStr:(NSString*)value{
str=value;
}
- retain:變量的setter方法釋放(release)舊的對象,然後将舊對象的值賦予輸入對象,再将輸入對象的索引計數增加1(retain):
@property (retain) NSString *str;
//----------------------------------------
-(void)setStr:(NSString*) v{
if(v != str){
[str release];
str=[v retain];
}
}
- copy:變量的setter方法進行Copy操作,與retain一樣,建立一個索引計數為1的對象,釋放掉舊對象:
@property (copy) NSString *str;
//-------------------------------------------
-(void)setStr:(NSString*) v{
if(v != str){
[str release];
str=[v copy];
}
}
- unsafe_unretained:等效于用__unsafe_unretaind關鍵字聲明的變量,在iOS 5.0之前的系統用該屬性代替weak來使用。
語意特性的使用方法:
- 隻要是值類型、簡單類型的類型,如NSInteger、CGPoint、CGFloat,以及C資料類型int、float、double,bool等,都應使用assign;
- 對于含有可深複制子類的對象,比如說NSArray、NSSet、NSDictionary、NSData、NSString等等,都應該使用copy特性;
- 對于NSMutableArray之類的可變類型,不能夠使用Copy特性,否則初始化會出現錯誤;
- 至于其他的NSObject對象,非ARC環境下都應該使用retain來進行操作,這也是絕大多數所使用的情況。
擁有者特性
對于ARC來說,上一節中所說的getter語意特性(assign、retain、copy)将被擁有者特性所代替。
- strong:strong相當于getter語意特性中的retain,strong特性的屬性将會成為對象的擁有者。這個特性稱之為強引用:
@property(strong) MyClass *myObject;
//Equal to:@property(retain) MyClass *myObject;
- weak:它聲明的屬性不會擁有這個對象的所有權,如果弱引用指向的對象被釋放的話,弱引用的對象會被自動設定為nil。weak比assign多一個自動把對象置為nil的步驟,稱為“歸零弱引用”(Zeroing Weak Reference)。
強引用與弱引用的廣義差別:
- 強引用也就是我們通常所講的引用,其存亡直接決定了所指對象的存亡。如果不存在指向一個對象的引用,則此對象會被從記憶體中釋放;
- 弱引用除了不決定對象的存亡外,其他與強引用相同。即使一個對象被持有無數個弱引用,隻要沒有強引用指向他,那麼它還是會被清除。弱引用的歸零特性為我們提供了保險,避免我們使用失效的引用。
原子特性
- atomic:原子性。原子性是資料庫原理裡面的一個概念,ACID中的第一個。在多線程中同一個變量可能被多個線程通路甚至更改造成資料污染,是以為了安全,Objective-C中預設是atomic的,會對對象的setter方法加鎖,相應的也會付出維護原子性(資料加鎖解鎖等)的系統資源代價。
- nonatomic: 非原子性。應用中如果不是特殊情況(多線程間的通訊程式設計),一般還是用nonatomic來修飾變量,不會對其setter方法加鎖,以提高多線程并發通路時的性能。
iOS開發的建議:
- 所有屬性都聲明為nonatomic;
- 盡量避免多線程搶奪同一塊資源;
- 盡量将加鎖、資源搶奪的業務邏輯交給伺服器端處理,減小移動用戶端的壓力。
0x03 ARC中的屬性關鍵字
屬性特性與記憶體管理關鍵字兩者是互斥的關系,在啟用ARC之後,屬性、關鍵字和對應的所有權關系如下:
屬性值 | 關鍵字 | 所有權 |
---|---|---|
strong | __strong | 有 |
weak | __weak | 無 |
unsafe_unretained | __unsafe_unretained | 無 |
copy | __strong | 有 |
assign | __unsafe_unretained | 無 |
retain | __strong | 有 |
- 屬性值一列都是用于聲明@property屬性聲明時用的;
- 編譯器編譯後會将這些屬性值轉換成對應的關鍵字;
- 關鍵字是用于聲明屬性清單或普通執行個體變量指針的。