分配到弱属性;对象将在赋值以后释放

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的时候,须要搞清楚意思,才能设置,不能随便设置,不然很容易致使程序死掉。
对于这部分的内容,目前尚未透彻理解,等有空的话,好好研究一下再总结出来。
相关文章
相关标签/搜索