天天看點

Objective-C 2.0之屬性(2)——屬性參數0x01 屬性聲明文法0x02 屬性參數0x03 ARC中的屬性關鍵字

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屬性聲明時用的;
  • 編譯器編譯後會将這些屬性值轉換成對應的關鍵字;
  • 關鍵字是用于聲明屬性清單或普通執行個體變量指針的。