nonatomic, retain,weak,strong用法詳解

strong weak

strong與weak是由ARC新引入的對象變量屬性
ARC引入了新的對象的新生命週期限定,即零弱引用。如果零弱引用指向的對象被deallocated的話,零弱引用的對象會被自動設置爲nil。

@property(strong) MyClass *myObject;
相當於@property(retain) MyClass *myObject;

@property(weak) MyOtherClass *delegate;
相當於@property(assign) MyOtherClass *delegate;

強引用與弱引用的廣義區別:
  強引用也就是我們通常所講的引用,其存亡直接決定了所指對象的存亡。如果不存在指向一個對象的引用,並且此對象不再顯示列表中,則此對象會被從內存中釋放。
  弱引用除了不決定對象的存亡外,其他與強引用相同。即使一個對象被持有無數個若引用,只要沒有強引用指向他,那麼其還是會被清除。沒辦法,還是 “強哥” 有面子。

簡單講strong等同retain
weak比assign多了一個功能,當對象消失後自動把指針變成nil,好處不言而喻。

__weak, __strong 用來修飾變量,此外還有 __unsafe_unretained, __autoreleasing 都是用來修飾變量的。
__strong 是缺省的關鍵詞。
__weak 聲明瞭一個可以自動 nil 化的弱引用。
__unsafe_unretained 聲明一個弱應用,但是不會自動nil化,也就是說,如果所指向的內存區域被釋放了,這個指針就是一個野指針了。
__autoreleasing 用來修飾一個函數的參數,這個參數會在函數返回的時候被自動釋放。


之前寫一個基於地理位置應用程序的時候無法讓應用程序打開定位功能,在.h文件裏面,我定義的屬性是這樣的:


@property(weak, nonatomic) CLLocationManager *locationManager;

後來,我將上面這句話改爲:

@property(nonatomic, retain) CLLocationManager *locationManager;

就可以了。

接下來,就詳細得介紹一下nonatomic, retain,weak,strong
在我們開發iOS程序時,常常會遇到:
property 和synthesize,以前很懶沒有仔細去理解,只是看了看別人寫的書,覺得挺容易的(在這裏我不得不說,現在很多本土出的土書,尤其是早期的2009年,寫的是真亂,誤人子弟),所以今天有時間,自己試驗了一番,希望和大家討論。property,他可以提供的功能有:提供成員變量的訪問方法的聲明、控制成員變量的訪問權限、控制多線程時成員變量的訪問環境 )。property不但可以在interface,在協議[url=]protocol[/url] .和類別[url=]category[/url]中也可以使用.synthesize的理解是:實現property所聲明的方法的定義。其實說直白就像是:property聲明瞭一些成員變量的訪問方法 ,synthesize則定義了由property聲明的方法。他們之前的對應關係是  property 聲明方法 ----------》 頭文件中申明的方法 synthesize定義方法---------》Cpp文件中定義的方法
不過這裏還有有一點細微的差別,後面我會講到。
先講property大家都知道:@property(attribute1 , attribute2, ...])是@property的他的官方表達方式,所以看到attribute1, attribute2,你就應該懂的, 他的用法不是很簡單。下面就對他的屬性列表進行分類介紹:下面對屬性列表進行一下簡單的介紹,後續會用代碼來解釋。1.可讀性:readonly 、readwrite@property(readwrite,....) valueType value;這個屬性是變量的默認屬性,就是如果你(readwrite and readonly都沒有使用,那麼你的變量就是readwrite屬性),通過加入readwrite屬性你的變量就會有get方法,和set方法。property(readonly,...) valueType value;這個屬性變量就是表明變量只有可讀方法,也就是說,你只能使用它的get方法。2,assign,setter方法直接賦值,不進行任何retain操作,爲了解決原類型與環循引用問題3,retain,setter方法對參數進行release舊值再retain新值,所有實現都是這個順序
4,copy,setter方法進行Copy操作,與retain處理流程一樣,先舊值release,再Copy出新的對象,retainCount爲1。這是爲了減少對上下文的依賴而引入的機制。
5,nonatomic,非原子性訪問,不加同步,多線程併發訪問會提高性能。注意,如果不加此屬性,則默認是兩個訪問方法都爲原子型事務訪問。鎖被加到所屬對象實例級.所以 不加nonatomic對與多線程是安全的 。 
其實他們都可以用代碼表示:1.nonatomic 和 atomic@property(nonatomic ) NSObject* test1;
@synthesize test1;上面兩句代碼,表示我們對test1的訪問,是非多線程安全的。@property(atomic) NSObject* test1;
@synthesize test1;上面兩句代碼,表示我們對test1的訪問,是多線程安全的。其實也就是在講該成員變量放到互斥代碼中,例如,下面進行加鎖。[_internal lock]; // lock using an object-level lock 
id result = [[value retain] autorelease]; 
[_internal unlock]; 
return result;

就如上面所說 ,這兩個屬性,是出於對多線程條件下 ,對test1的訪問安全。如果你的程序的成員變量不存在安全問題,用nonatomic 就好,因爲這樣不要在訪問是進行互斥,效率更高。
2. readonly 、readwrite (注,後續過程我們都會加入nonatomic 屬性,因爲它是十分普遍的)2.1 readonly@property(nonatomic  ,readonly) NSObject* test1;
@synthesize test1;上面的兩句代碼,objc編輯器將會爲我們翻譯爲:@property(nonatomic  ,readonly) NSObject* test1; 等同-(NSObject*)test1;
@synthesize test1;等同-(NSObject*)test1{ return test1;}2.2 readwrite 
@property(nonatomic  ,readwrite ) NSObject* test1;
@synthesize test1;上面的兩句代碼,objc編輯器將會爲我們翻譯爲:@property(nonatomic  ,readwrite ) NSObject* test1; 等同-(NSObject*)test1;
-(void)settest1(NSObject* other);@synthesize test1;等同-(NSObject*)test1{ return test1;}-(void)settest1(NSObject* other);
{ test1 = other;}
這裏要說明一下,
readonly 、readwrite 這兩個屬性他們的真正價值,不是提供成員變量訪問接口,而是控制
成員變量的訪問權限。所以要抓住他們真正價值。3. assign@property(nonatomic  ,assign) NSObject* test1;@synthesize test1;上面兩句:objc編輯器將會翻譯如下:@property(nonatomic  ,assign) NSObject* test1;等同
-(void)settest1(NSObject* other);@synthesize test1;-(void)settest1(NSObject* other);等同
{ test1 = other;}
4. retain@property(nonatomic  ,retain) NSObject* test1;
@synthesize test1;
objc編輯器翻譯如:
@property(nonatomic  ,retain) NSObject* test1;等同-(NSObject*)test1;
-(void)settest1(NSObject* other);
@synthesize test1;

-(NSObject*)test1{ return test1;}-(void)settest1(NSObject* other) {    if (test1!= other)    {                   [test1release];                   test1= [otherretain];     }}
5. copy@property(nonatomic  ,copy) NSObject* test1;
@synthesize test1;
objc編輯器將翻譯如:
@property(nonatomic  ,copy) NSObject* test1;
-(NSObject*)test1;
-(void)settest1(NSObject* other);
@synthesize test1;
-(NSObject*)test1{ return test1;}-(void)settest1(NSObject* other);
{
    if (test1!=other) { 
        [test1release]; 
        test1= [othercopy]; 
    }
}對於Copy屬性有一點要主要,被定義有copy屬性的對象必須要符合NSCopying協議,並且你還必須實現了-(id)copyWithZoneNSZone*)zone該方法


代碼纔是王道:都是一些簡單代碼用例//爲了更具有普遍性,我選擇用自定義對象testObj /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//testObj .h
@interface testObj : NSObject<NSCopying>{
    
}
@end
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//testObj .m-(id)copyWithZoneNSZone *)zone
{
    testObj* pObj = [[testObj allocWithZone:zone] init];
    return pObj;
}
@end
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////testApp是包含多個testObj 對象指針//testApp .h
@interface testApp :
{
    testObj*  test1;
    testObj*  _test2;
}

@property(nonatomic , retain) NSObject* test1;
@property(nonatomic , copy) NSObject* test2;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//testApp.m@synthesize test1;@synthesize test2 = _test2;//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@synthesize test2 = _test2; //對這裏要特別主要一下,這裏可以看作是一種別名機制,這點和前面類比的C++頭文件和Cpp文件不同。
例如對於setter函數,objc編輯器將會按如下方式翻譯如果是@synthesize _test2;setter函數將是這種形式:-(void)set_test2(NSObject*); //注意中間的下劃線寫成@synthesize test = _test2;setter函數將是這種形式:
-(void)settest2(NSObject*);// _test2 被 test2替換了
可以看出這種別名機制,感覺是規範書寫(其實更像是規範Objc的書寫,大家可以看看官方文檔中,成員變量都是前面帶下滑線的(如_test2),所以才搞了這樣一個別名規範代碼中的書寫)
//下面是一個功能函數,-(void )test{test1 = [[testObj alloc]init];// test1  retainCount  =1;

//下面有三種對test2操作方法: _test2 = test1; //這裏是將test1的指針賦值給_test2指針,注意,並沒有調用test2的setter方法 ,所以test retainCount  = 1、 test2 retainCount  = 0; self.test2 = test1;  //這裏調用test2的Copy方法,因此這是test retainCount  = 1、 test2 retainCount  = 1;   test2   = test1; //這段代碼系統將會提示出錯說test2沒有定義。因爲這裏編譯器認爲是一條賦值表達式,將test2看作是一個成員變量,而在我們的testApp 中是沒有這個成員變量的,這裏要區別我們所說的別名,別名機制可以看作是在調用setter或者getter函數纔會起作用,而這裏只是一個簡單的賦值,也就出現未定義的錯誤。如果沒有理解,你就記住要調用setter或者getter函數,就用"self.成員變量"這種形式就行了.
//我們把  test2   = test1這行代碼註釋掉,保證程序繼續執行    [test1 release];  // 釋放test1 ,test1 retainCount  = 0;
    
    [test2 release]; // 釋放test2 ,test2 retainCount  = 0;
}@end

發佈了11 篇原創文章 · 獲贊 21 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章