分配到弱屬性;對象將在賦值以後釋放

BirdWatching的iOS app,如今想要去多多折騰,搞懂不一樣property的setter修飾符:assign,copy,retain等的更深層的含義。
因此,專門去把代碼改成:
?
123    //@property (nonatomic, weak) UIImagePickerController *imgPickerController;//@property (nonatomic) UIImagePickerController *imgPickerController;@property (nonatomic, assign) UIImagePickerController *imgPickerController;    
改了以後,結果就是.m文件中初始化的代碼:
?
1    self.imgPickerController = [[UIImagePickerController alloc] init];    
出現了警告:
ARC Semantic Issue,Assigning retained object to unsafe property;object will be released after assignment,而後程序運行,也出錯了:
 
【解決過程】
1.根據以前的學習,對於assign等setter的含義爲:
The Objective-C Programming Language – Declared Properties
Setter Semantics
These attributes specify the semantics of a set accessor. They are mutually exclusive. 
strong
Specifies that there is a strong (owning) relationship to the destination object.
weak
Specifies that there is a weak (non-owning) relationship to the destination object.
If the destination object is deallocated, the property value is automatically set to nil.
(Weak properties are not supported on OS X v10.6 and iOS 4; use assigninstead.)
copy
Specifies that a copy of the object should be used for assignment. 
The previous value is sent a release message.
The copy is made by invoking the copy method. This attribute is valid only for object types, which must implement the NSCopying protocol.
assign
Specifies that the setter uses simple assignment. This attribute is the default.
You use this attribute for scalar types such as NSInteger and CGRect.
retain
Specifies that retain should be invoked on the object upon assignment.
The previous value is sent a release message.
In OS X v10.6 and later, you can use the __attribute__ keyword to specify that a Core Foundation property should be treated like an Objective-C object for memory management:
@property(retain) __attribute__((NSObject)) CFDictionaryRef myDictionary;
因此,此處代碼改成:
?
1    @property (nonatomic, assign) UIImagePickerController *imgPickerController;    
後,加上以前對於assign的學習,知道了此處對於assign,首先是隻適用於非對象類的數據,好比NSInteger,也就是,對於對象引用計數的話,沒有任何改變。
因此,上述的初始化代碼部分中的:
[[UIImagePickerController alloc] init];
獲得了一個UIImagePickerController,而後賦值給了
self.imgPickerController
可是要知道,此處的self.imgPickerController因爲是assign,因此沒有對於上述獲得的UIImagePickerController引用計數增長,即沒有打算再用到UIImagePickerController,因此剛生成的UIImagePickerController,由於沒有人再用刀它,就自動釋放掉了。因此後續對於self.imgPickerController的操做,都是在操做一個沒有分配實體對象的空的指針,因此確定都是無效操做,確定就會出現EXC_BAD_ACCESS錯誤了。
就是以前C語言中的野指針的意思了,只是有個指針變量而已,而指針所指向的物理內存,早已被釋放掉了,因此你再繼續操做此塊物理內存,就會出現異常操做了。
 
2.對應的,去改成:
?
1234    //@property (nonatomic, weak) UIImagePickerController *imgPickerController;//@property (nonatomic) UIImagePickerController *imgPickerController;//@property (nonatomic, assign) UIImagePickerController *imgPickerController;@property (nonatomic, retain) UIImagePickerController *imgPickerController;    
而後運行結果就是OK的了。
 
3.相應的,也基本明白了,以前對於寫成weak的話:
?
1234    @property (nonatomic, weak) UIImagePickerController *imgPickerController;//@property (nonatomic) UIImagePickerController *imgPickerController;//@property (nonatomic, assign) UIImagePickerController *imgPickerController;//@property (nonatomic, retain) UIImagePickerController *imgPickerController;    
而後對於此處的和imgPickerController所有相關的代碼是:
?
12345678910111213141516171819    - (void)viewDidLoad{    self.imgPickerController = [[UIImagePickerController alloc] init];    self.imgPickerController.delegate = self; }     //handle the tap to image-(void)handleImageTap:(UITapGestureRecognizer *)sender{         if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum])    {        NSArray *availableMediaTypeArr = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum];                 self.imgPickerController.mediaTypes = availableMediaTypeArr;        [self presentViewController:self.imgPickerController animated:YES completion:NULL];    }}    
其中,運行到handleImageTap的時候,在view畫面切換的時候:
?
1    [self presentViewController:self.imgPickerController animated:YES completion:NULL];    
程序會出錯。
因此,內部的邏輯,應該是:
最開始用:
?
1    self.imgPickerController = [[UIImagePickerController alloc] init];    
初始化後,就是一個weak弱引用了,意思是,若是後者,即alloc的UIImagePickerController被dealloc的話,
那麼前者self.imgPickerController就自動設置爲nil了。
可是因爲當前程序,顯示控件中,一直使用到了UIImagePickerController,因此一直也沒什麼問題。
可是當調用presentViewController畫面切換的時候,就自動去dealloc釋放了,那個引用爲0的,以前alloc的UIImagePickerController,因此,致使此時self.imgPickerController也就自動變爲nil了。
因此程序會出錯了。
4.對應的,當程序再改成:
?
1234    //@property (nonatomic, weak) UIImagePickerController *imgPickerController;@property (nonatomic) UIImagePickerController *imgPickerController;//@property (nonatomic, assign) UIImagePickerController *imgPickerController;//@property (nonatomic, retain) UIImagePickerController *imgPickerController;    
後,此時默認的是strong,即owning的效果了,因此引用計數爲1了,因此即便畫面切換,因爲self.imgPickerController對於UIImagePickerController的引用計數仍是1,沒有變爲0,因此,後續的self.imgPickerController指針指向的,是真正存在的UIImagePickerController對象,因此程序能夠正常執行的。
即默認的strong和retain的效果是一致的。
5.對應的,後來也看到:
iOS 5中的strong和weak關鍵字 
中說,weak至關於assign,strong至關於retain,可是看着仍是很暈。
6.最後看到這裏:
Weak and strong property setter attributes in Objective-C
解釋的很清楚:
對於單個文件,根據設置,能夠開啓或關閉ARC。
若是用了ARC,則不能使用retain,release,autorelease等等修飾符,而只能:
針對屬性property,使用weak,strong;
針對變量variable,使用__weak,__strong;
strong等價於retain;
weak等價於assign;
 
只有一種狀況下須要用到weak:
當你想要避免循環引用的時候,纔會考慮用weak;
由於若是都用strong的話,有可能出現,父類retain子類,而子類retain父類,即循環引用了,致使二者始終都沒法釋放。
此時就能夠用weak避免此狀況。
 
另外還有個toll free bridging的部分,很少解釋,有空看官網解釋:
Core Foundation Design Concepts – Toll-Free Bridged Types
 
而關於此部分的內容,相關的官網解釋,在這裏:
Transitioning to ARC Release Notes

【總結】
在對於變量/屬性,設置strong仍是weak,是assign仍是retain,仍是copy的時候,須要搞清楚意思,才能設置,不能隨便設置,不然很容易致使程序死掉。
對於這部分的內容,目前尚未透徹理解,等有空的話,好好研究一下再總結出來。
相關文章
相關標籤/搜索