【问题】
BirdWatching的iOS app,现在想要去多多折腾,搞懂不同property的setter修饰符:assign,copy,retain等的更深层的含义。
所以,专门去把代码改为:
123 | //@property //@property @property |
改了之后,结果就是.m文件中初始化的代码:
1 | self.imgPickerController |
出现了警告:
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
所以,此处代码改为:
1 | @property |
后,加上之前对于assign的学习,知道了此处对于assign,首先是只适用于非对象类的数据,比如NSInteger,也就是,对于对象引用计数的话,没有任何改变。
所以,上述的初始化代码部分中的:
[[UIImagePickerController alloc] init];
得到了一个UIImagePickerController,然后赋值给了
self.imgPickerController
但是要知道,此处的self.imgPickerController由于是assign,所以没有对于上述得到的UIImagePickerController引用计数增加,即没有打算再用到UIImagePickerController,所以刚生成的UIImagePickerController,因为没有人再用刀它,就自动释放掉了。所以后续对于self.imgPickerController的操作,都是在操作一个没有分配实体对象的空的指针,所以肯定都是无效操作,肯定就会出现EXC_BAD_ACCESS错误了。
就是之前C语言中的野指针的意思了,只是有个指针变量而已,而指针所指向的物理内存,早已被释放掉了,所以你再继续操作此块物理内存,就会出现异常操作了。
2.对应的,去改为:
1234 | //@property //@property //@property @property |
然后运行结果就是OK的了。
3.相应的,也基本明白了,之前对于写成weak的话:
1234 | @property //@property //@property //@property |
然后对于此处的和imgPickerController全部相关的代码是:
12345678910111213141516171819 | - void )viewDidLoad { self.imgPickerController = [[UIImagePickerController alloc] init]; self.imgPickerController.delegate = self; } //handle -( 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 |
程序会出错。
所以,内部的逻辑,应该是:
最开始用:
1 | self.imgPickerController |
初始化后,就是一个weak弱引用了,意思是,如果后者,即alloc的UIImagePickerController被dealloc的话,
那么前者self.imgPickerController就自动设置为nil了。
但是由于当前程序,显示控件中,一直使用到了UIImagePickerController,所以一直也没什么问题。
但是当调用presentViewController画面切换的时候,就自动去dealloc释放了,那个引用为0的,之前alloc的UIImagePickerController,所以,导致此时self.imgPickerController也就自动变为nil了。
所以程序会出错了。
4.对应的,当程序再改为:
1234 | //@property @property //@property //@property |
后,此时默认的是strong,即owning的效果了,所以引用计数为1了,所以即使画面切换,由于self.imgPickerController对于UIImagePickerController的引用计数还是1,没有变为0,所以,后续的self.imgPickerController指针指向的,是真正存在的UIImagePickerController对象,所以程序可以正常执行的。
即默认的strong和retain的效果是一致的。
5.对应的,后来也看到:
中说,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的时候,需要搞清楚意思,才能设置,不能随便设置,否则很容易导致程序死掉。
对于这部分的内容,目前还没有透彻理解,等有空的话,好好研究一下再总结出来。