iOS 屬性修飾符的區別 – iPhone手機開發 iPhone軟體開發教學課程

前言
iOS5 之前 所有的 開發都需要開發者自己控制自己的對象的引用和釋放。使用的修飾符是 assign、copy、retain
iOS5 之後,Apple 推出瞭ARC(自動引用計數)機制,推出瞭新的修飾符替代之前的修飾符 strong、weak

簡單說明
1:ARC環境下,strong代替retain.weak代替assign
2:weak的作用:在ARC環境下,,所有指向這個對象的weak指針都將被置為nil。這個T特性很有用,相信很多開發者都被指針指向已釋放的對象所造成的EXC_BAD_ACCESS困擾過,使用ARC以後,不論是strong還是weak類型的指針,都不會再指向一個已經銷毀的對象,從根本上解決瞭意外釋放導致的crash。
3:assign的作用:簡單賦值,不改變引用計數,對基礎數據類型 (例如NSInteger,CGFloat)和C數據類型(int, float, double, char, 等) 適用簡單數據類型
4:copy的作用:建立一個索引計數為1 的對象,然後釋放舊對象
5:strong的作用:在ARC環境下,隻要某一對象被一個strong指針指向,該對象就不會被銷毀。如果對象沒有被任何strong指針指向,那麼就會被銷毀。在默認情況下,所有的實例變量和局部變量都是strong類型的。可以說strong類型的指針在行為上跟非ARC下得retain是比較相似的
6:retain的作用:在非ARC時代,你需要自己retain一個想要保持的對象,ARC環境下就不需要瞭。現在唯一要做的就是用一個指針指向這個對象,隻要指針沒有被重置為空,對象就會一直在堆上。當指針指向新值的時候,原來的對象就會被release一次。這對實例變量,sunthesize的變量或者是局部變量都是實用的。

1: 什麼是assign,copy,retain之間的區別?

assign: 簡單賦值,不更改索引計數(Reference Counting)。

copy:一個對象變成新的對象(新內存地址) 引用計數為1 原來對象計數不變

retain:釋放舊的對象,將舊對象的值賦予輸入對象,再提高輸入對象的索引計數為1

2.__weak, __unsafe_unretained, __autoreleasing,的區別
__weak:聲明瞭一個可以自動 nil 化的弱引用。
__unsafe_unretained:聲明一個弱應用,但是不會自動nil化,也就是說,如果所指向的內存區域被釋放瞭,這個指針就是一個野指針瞭。
__autoreleasing:用來修飾一個函數的參數,這個參數會在函數返回的時候被自動釋放。

3: weak 和strong的區別:

強引用也就是我們通常所講的引用,其存亡直接決定瞭所指對象的存亡。如果不存在指向一個對象的引用,並且此對象不再顯示列表中,則此對象會被從內存中釋放。
弱引用除瞭不決定對象的存亡外,其他與強引用相同。即使一個對象被持有無數個若引用,隻要沒有強引用指向他,那麼其還是會被清除。
簡單講,strong等同retain,weak比assign多瞭一個功能,當對象消失後自動把指針變成nil。

strong:是缺省的關鍵詞。

(weak和strong)不同的是 當一個對象不再有strong類型的指針指向它的時候 它會被釋放 ,即使還有weak型指針指向它。

一旦最後一個strong型指針離去 ,這個對象將被釋放,所有剩餘的weak型指針都將被清除。

可能有個例子形容是妥當的。

想象我們的對象是一條狗,狗想要跑掉(被釋放)。

strong型指針就像是栓住的狗。隻要你用牽繩掛住狗,狗就不會跑掉。如果有5個人牽著一條狗(5個strong型指針指向1個對象),除非5個牽繩都脫落 ,否著狗是不會跑掉的。

weak型指針就像是一個小孩指著狗喊到:“看!一隻狗在那” 隻要狗一直被栓著,小孩就能看到狗,(weak指針)會一直指向它。隻要狗的牽繩脫落,狗就會跑掉,不管有多少小孩在看著它。

隻要最後一個strong型指針不再指向對象,那麼對象就會被釋放,同時所有的weak型指針都將會被清除。

歸零弱引用
即在該引用指向的對象釋放後,這些弱引用就會被設置為nil。要使用歸零弱引用必須明確的聲明它們。有兩種方式可以聲明歸零弱引用:聲明變量時使用_weak關鍵字或對屬性使用weak特性(如下)。
[objc] view plain copy print?
__weak NSString *myString;
@property (weak) NSString *myString;
如果想在不支持弱引用的舊系統上使用ARC,可以使用__unsafe_unretained關鍵字和unsafe_unretained特性,它們會告訴ARC這個特殊的引用是弱引用。
在使用ARC的時候有兩種命名規則需要註意:
1、屬性名稱不能以new開頭,比如
[objc] view plain copy print?
@property NSString *newString;
是不被允許的。
2、屬性不能隻有一個read-only而沒有內存管理特性。如果你沒有啟用ARC,可以使用
[objc] view plain copy print?
@property (readonly) NSString *title;
語句,但如果你啟用瞭ARC功能,就必須制定由誰來管理內存。因為默認的特性是assign,所以你可以進行一個簡單的修復,使用unsafe_unretained就可以瞭

使用場景

1: 使用assign: 對基礎數據類型 (NSInteger,CGFloat)和C數據類型(int, float, double, char, 等等)

2: 使用copy: 對NSString

3: 使用retain: 對其他NSObject和其子類

4: strong 和weak

strong 用來修飾強引用的屬性;

@property (strong) SomeClass * aObject;

對應原來的
@property (retain) SomeClass * aObject; 和 @property (copy) SomeClass * aObject;

weak 用來修飾弱引用的屬性;
@property (weak) SomeClass * aObject;
對應原來的

@property (assign) SomeClass * aObject;

5: nonatomic關鍵字:

atomic是Objc使用的一種線程保護技術,基本上來講,是防止在寫未完成的時候被另外一個線程讀取,造成數據錯誤。而這種機制是耗費系統資源的,所以在iPhone這種小型設備上,如果沒有使用多線程間的通訊編程,那麼nonatomic是一個非常好的選擇。

補充基礎知識

IOS的對象都繼承於NSObject, 該對象有一個方法:retainCount ,內存引用計數。 引用計數在很多技術都用到: window下的COM組件,多線程的信號量,讀寫鎖,思想都一樣。

(一般情況下: 後面會討論例外情況)
alloc 對象分配後引用計數為1
retain 對象的引用計數+1
copy copy 一個對象變成新的對象(新內存地址) 引用計數為1 原來對象計數不變

release 對象引用計數-1 如果為0釋放內存
autorelease 對象引用計數-1 如果為0不馬上釋放,最近一個個pool時釋放
NSLog(@”sMessage retainCount:%u”,[sMessage retainCount]);

內存管理的原則就是最終的引用計數要平衡,
如果最後引用計數大於0 則會內存泄露
如果引用 計數等於0還對該對象進行操作,則會出現內存訪問失敗,crash 所以盡量設置為nil
這兩個問題都很嚴重,所以請一定註意內存釋放和不用過後設置為nil

成員變量與屬性
實際情況並非上面那麼簡單,你可能需要在一個函數裡調用另一個函數分配的變量這時候
有兩個選擇: 類成員變量和使用屬性
@interface TestMem: NSObject {
TestObject *m_testObject ; // 成員變量
TestObject *testObject; //成員變量
}
成員變量與上面的內存管理是一致的,隻是在不同的函數裡要保持引用計數加減的平衡
所以要你要每次分配的時候檢查是否上次已經分配瞭。是否還能調用
什麼時候用屬性?
1. 把成員做為public.
2. outlet 一般聲明為屬性( 這個內存於系統控制,但我們還是應該做一樣操作,後面會講)
3. 如果很多函數都需要改變這個對象 ,或這個函數會觸發很多次,建議使用屬性。我們看看屬性函數展開後是什麼樣子:

// assign
-(void)setTestObject :(id)newValue{
testObject= newValue;
}
// retain
-(void)setTestObject :(id)newValue{
if (testObject!= newValue) {
[testObject release];
testObject= [newValue retain];
}
}
// copy
-(void)setTestObject :(id)newValue{
if (testObject != newValue) {
[testObject release];
testObject = [newValue copy];
}
}
asssign 相於於指針賦值,不對引用計數進行操作,註意原對象不用瞭,一定要把這個設置為nil
retain 相當於對原對象的引用計數加1
copy 不對原對象的引用計數改變,生成一個新對象引用計數為1
註意:
self.testObject 左值調用的是setTestObject 方法. 右值為get方法,get 方法比較簡單不用說瞭
而 真接testObject 使用的是成員變量
self.testObject = [[testObject alloc] init]; // 錯 reatin 兩次
testObject = [NSArray objectbyindex:0]; //錯 不安全,沒有retain 後面release會出錯
如果testObject已有值也會mem leak

自動管理對象
IOS 提供瞭很多static(+) 創建對象的類方法,這些方面是靜態的,可以直接用類名
調用如:
NSString *testString = [NSString stringWithFormat:@”test” ];
testString 是自動管理的對象,你不用relese 他,他有一個很大的retain count, release後數字不變。

例外
有一些通過alloc 生成的對象相同是自動管理的如:
NSString *testString = [[NSString alloc] initWithString:@”test1”];
retain count 同樣是很大的數,沒辦法release
但為瞭代碼對應,還是應該加上[ testString release];
不然xcode的Analyze 會認識內存leak, 但Instruments leak 工具檢測是沒有的

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *