写这篇文章的缘由,是因为我泡在stackoverflow上翻帖子,看到一个名为Should IBOutlets be strong or weak under ARC?
有人问,在ARC下,IBOutlets到底应该定义成strong 还是 weak ?支持这个答案的人最多,答案仅是摘自官方文档的一个片段:
From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because:
-
Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.
-
The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).
@property (weak) IBOutlet MyView *viewContainerSubview; @property (strong) IBOutlet MyOtherClass *topLevelObject;
大意是说,在
什么是
以上的分析都没有错,但是总觉得少了点什么。对于到底是weak 还是 strong,归根结底,还是要刨到对对象所有权的问题上,但是不便于总结出浅显易懂的规律性使用法则。于是,就会有一个又一个的特例打破文档所总结的常规,不明白规则的根是什么,还是会碰到麻烦的。
我来举一个简单的例子,创建一个程序入口指向navigation controller的工程,导航栏上拖2个按钮:
右侧按钮用于控制相机按钮的显示与否,按照文档的指示,我们在程序中定义这两个按钮应为weak属性
- #import
-
- @interface
TestViewController : UIViewController - {
-
BOOL isShowing; - }
-
- @property
(nonatomic,weak)IBOutlet UIBarButtonItem *controlBtn; - @property
(nonatomic,weak)IBOutlet UIBarButtonItem *cameraBtn; -
- -(IBAction)controlAction:(id)sender;
- @end
用右侧按钮,控制相机按钮的隐藏和显示:
- #import
"TestViewController.h" -
- @interface
TestViewController () -
- @end
-
- @implementation
TestViewController - @synthesize
cameraBtn,controlBtn; -
- -
(void)viewDidLoad - {
-
[super viewDidLoad]; -
// Do any additional setup after loading the view, typically from a nib. -
isShowing = YES; - }
-
- -
(void)viewDidUnload - {
-
[super viewDidUnload]; -
// Release any retained subviews of the main view. - }
-
- -
(BOOL)shouldAutorotateToInterf aceOrientation:(UIInterfaceOrientation)interfaceOrientation - {
-
return (interfaceOrientation != UIInterfaceOrientationPo rtraitUpsideDown); - }
-
- -(IBAction)controlAction:(id)sender
- {
-
if (isShowing) { -
self.controlBtn.title = @"显示相机"; -
self.navigationItem.leftBarButtonItem = nil; -
isShowing = NO; -
}else { -
self.controlBtn.title = @"隐藏相机"; -
self.navigationItem.leftBarButtonItem = cameraBtn; -
isShowing = YES; -
} - }
- @end
实验结果是,第一次隐藏了相机按钮后,就再也显示不出来了。原因很简单,cameraBtn指向了空,我们丢失了cameraBtn的对象所有权。
解决问题的办法有两个:
1.不在xib或者storyboard上拖相机按钮,而是用代码创建,自己控制对象所有权
2.将 cameraBtn 定义为strong
我想强调的当然是第二种方法,当然了,改成strong后,相应的也需要配合ARC做下工作:
- -
(void)viewDidUnload - {
-
[super viewDidUnload]; -
// Release any retained subviews of the main view. -
self.cameraBtn = nil; - }
顺便提一下ARC其他属性的规则:
-
strong:等同于"retain",属性成为对象的拥有者
-
weak:属性是 weak pointer,当对象释放时会自动设置为 nil
-
unsafe_unretained:等同于之前的"assign",只有 iOS 4 才应该使用
-
copy:和之前的 copy 一样,复制一个对象并创建 strong 关联
-
assign:对象不能使用 assign,但原始类型(BOOL、int、float)仍然可以使用