iOS开发使用strong应该优先于weak原因

起因

最近来到新公司产生了控件使用strong&weak问题的讨论。项目中所有控件包括咨询的很多人都习惯的使用strong,并且有Storyboards拖得控件使用weak,代码创建的使用strong的习惯。而我一直控件使用的weak。所以产生了讨论。

总述

正如大家知道的,如果一个对象没有被强引用就会被自动释放,那么我们还未什么要在控件使用weak?控件不会被释放?控件还可以正常显示?

首先我们大致的分三种情况

  1. 从storyboard或者xib创建
  2. 手动创建声明为weak
  3. 手动创建声明为strong

从storyboard或者xib创建


当一个控件被放在View上时,就已经存在了如下的一个拥有关系:UIViewController->UIView->subView->(控件)
然后你为这个控件声明一个weak属性


@property(nonatomic,weak) IBOOutlet UIButton *btn;




相当于storyboard或者xib对这个控件是强引用,你声明的属性对它是弱引用。


手动创建并声明strong

@property(nonatomic,strong) UIButton *btn;




那么你在实现这个控件时只需这样:


_btn = [[UIButton alloc]init];
[self.view addSubview:_btn]`


### 手动创建并申明为weak

<div class="highlight">
<pre>`@property(nonatomic,weak) UIButton *btn;
`</pre>
</div>
那么你在实现这个控件时需要这样:
<div class="highlight">
<pre>`UIButton *button = [[UIButton alloc]init];
_btn = button;
[self.view addSubview:_btn];

其实不管声明的属性是强引用还是弱引用,在控制器消失的时候,这个属性消失,View消失,subViews消失,控件也就消失了。

也许你会觉得似乎用strong更简单。

## 说明

IBOutlet的属性一般可以设为weak是因为它已经被view引用了,除非view被释放,否则IBOutlet的属性也不会被释放,另外IBOutlet属性的生命周期和view应该是一致的,所以IBOutlet属性一般设为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).
简单的说,如果IBOutlet对象是nib/sb scene的拥有者(File’s owner)所持有的对象,那么很显然拥有者必须“拥有”对象的指针,因此属性应设置为strong。而其他的IBOutlet对象的属性需要设置为weak,因为拥有者并不需要“拥有”他们的指针。举例来说,UIViewController的view属性是strong,因为controller要直接拥有view。而添加到view上的subviews,作为IBOutlet只需要设置为weak就可以了,因为他们不是controller直接拥有的。直接拥有subviews的是controller的view,ARC会帮助管理内存。

紧接着,文档里又提到:
> Outlets should be changed to strong when the outlet should be considered to own the referenced object:
>
>
> As indicated previously, this is often the case with File’s Owner—top level objects in a nib file are frequently considered to be owned by the File’s Owner.
>
You may in some situations need an object from a nib file to exist outside of its original container. For example, you might have an outlet for a view that can be temporarily removed from its initial view hierarchy and must therefore be maintained independently.
第一种情形前面已经解释过了,对于第二种,通俗点将,就是controller需要直接控制某一个subview并且将subview添加到其他的view tree上去。

单纯从ARC的角度思考,用weak也是很显然的:因为subview添加到view上时,view会“拥有”subview。当然,给IBOutlet属性设置为strong也没有错,“纠结谁对谁错“的问题可能需要上升到模式或者编码习惯的问题,已经超出本文的范围。

## 理解

首先,控件的父(( view.superview ))已经 “强” 的抓着他,所以你只需要 “弱弱” 的看着他就好。因为 “

addSubview“的原因。 并且当控件从 父 上移除,就自动销毁,逻辑上也是正确的。

另外,如果你需要控件再View的SuperView里面remove以后还是继续存在(重复添加),就好比你不管控件的父(( view.superview ))已经 “强” 的抓着他,反正你要 “强强”的抓住他,需要控件的时候就立马把他抓过来用,这个时候你就需要用强。
> 这个只是个人理解观点,如果有不妥还请指正。

## Strong 引用的 IBOutlet

Apple已经对Xib和Storyboard文件做了很多优化。并且由于这些优化,你现在可以将IBOutlet定义为strong,而不是weak。Apple曾在上一届的WWDC上指出这一点,因此让我们来看一下其中的更多细节。你可以从 这个文档 中找到管理Nib文件中对象生命周期的章节:

Outlet一般来说应该为weak,除了在nib文件 ( 或者iOS中,storyboard scene) 中的File’s Owner的顶级对象,这个对象可以是strong。你创建的Outlets应该为weak,原因如下:

你创建的一个 view controller 视图的子视图或者 window controller 窗体视图的 Outlets,是对象之间的弱引用,不应该有依赖关系。 strong的outlet通常是特殊的framework类(如:UIViewController 视图的 outlet,或者 NSWindowController 视窗的 outlet)。
正如这个段落所解释的一样,view controller视图的子视图 outlet应该为 weak,因为这个视图已经被nib文件的顶级对象所拥有了。然而,当一个Outlet被定义为weak指针时,ARC会在编译期间调用以下函数:


id objc_storeWeak(id object, id value);



这个函数把对象的值作为key,并把它添加到table中。这个table被称为weak table。ARC使用这个table去存储应用中的所有的weak指针。现在,当对象被deallocated时,ARC将会指向weak table并且将weak引用置为nil。同时,ARC将会调用:


void objc_destroyWeak(id  object);



紧接着,注销这个对象并再次调用objc_destroyWeak:


objc_storeWeak(id *object, nil);



这种weak引用关联的生命周期是strong引用的2-3倍。所以,通过避免简单地定义outlets为strong,使用弱引用是一种运行期间的通用做法。

我想这个决策与已废弃的viewDidUnload方法有关。直到iOS 5,这个方法被用于清空在低内存环境下的视图。正如文档中解释的那样:

在iOS 5之前,当发生低内存警告或者当前view controller的视图不被需要时,在视图被释放之后,系统会选择性地调用这个方法。这个方法让你可以进行最后的清理工作。如果你的视图存储了视图或者其子视图的单独引用,你应该使用这个方法去释放这些引用。

在那时,定义一个属性为weak是有意义的,因为这就不用在viewDidUnload额外地释放对象。但是在iOS 9中,我相信我们已经有足够的时间去避免使用这个方法。因此,在IBOutlets定义weak是没意义的。

来源:http://www.cocoachina.com/ios/20151026/13868.html

## 最新理解

所以最新的建议是:都用strong!