iOS5 ARC,IBOutlets 应当定义strong还是weak

来源:互联网 发布:淘宝客服沟通技巧ppt 编辑:程序博客网 时间:2024/05/17 02:12


http://www.myexception.cn/operating-system/681341.html



写这篇文章的缘由,是因为我泡在stackoverflow上翻帖子,看到一个名为Should IBOutlets be strong or weak under ARC? 的帖子很热,而我对被采纳为标准答案的回答也有一些话要补充,我想对于每一个初识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;

大意是说,ARC 中,一般outlet属性都推荐使用weak,应该使用 strongoutletFile's Owner连接到 nib的顶层对象。 

什么是 File's Owner连接到 nib 的顶层对象呢?说白话一点,就是自定义的view,不是直接作为main view里面一个sub view直接显示出来,而是需要通过实例化创建出来的。你自己实例化,当然需要strong了,不然谁还替你保留对象所有权呢?


以上的分析都没有错,但是总觉得少了点什么。对于到底是weak 还是 strong,归根结底,还是要刨到对对象所有权的问题上,但是不便于总结出浅显易懂的规律性使用法则。于是,就会有一个又一个的特例打破文档所总结的常规,不明白规则的是什么,还是会碰到麻烦的。


我来举一个简单的例子,创建一个程序入口指向navigation controller的工程,导航栏上拖2个按钮:


右侧按钮用于控制相机按钮的显示与否,按照文档的指示,我们在程序中定义这两个按钮应为weak属性

#import <UIKit/UIKit.h>@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)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);}-(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)仍然可以使用


  • strong
  • weak
  • unsafe_unretained
  • autoreleasing

    其中前三个可以用于属性中声明所有权。对于这三个用于属性声明的修饰符,下面分别讲讲他们对应的使用场景。

    1.strong
    strong与之前的retain差不多,可以增加属性引用计数的值。在不需要的时候,需要手动设置属性为nil。

    2.weak
    weak是iOS5及以上才支持的修饰符。它被称为“归零弱引用”。可以只是持有指针而不增加引用计数来避免循环保留。当指针指向的内存被销毁后,声明weak的属性指针会自动置为nil,这也是它被称为归零弱引用的原因。

    3.unsafe_unretained
    对于iOS5以下版本,并不支持ARC中的weak声明,可以用unsafe_unretained声明来代替weak。unsafe_unretained同样也是不增加引用计数的值,但它没有“归零”的动作,需要手动操作,一般作为支持iOS5以下weak的替代方案。

    解释了3种属性的意义,下面说说对于属性声明为IBOutlet时3种所有权修饰符的取舍以及使用技巧。
    由于我自己很熟悉Interface Bulider中的操作,所以非常喜欢那种拉控件然后连线并直接声称属性代码的操作。对于直接从xib或者storyboard拉出来生成的IBOutlet属性,一般是选择strong还是weak呢?

    这里有个原则

    如果该控件位于控件树的顶部,比如UIViewController下的view,那就应该选择strong,因为viewcontroller直接拥有该view。例如右图中的View。

    而如果控件是viewcontroller中view的子视图,对于这个子视图,它的所有者是它的父视图,代码中只是想引用一下这个子视图的指针而已,那么就应该选择weak(iOS5以下选择unsafe_unretained)。例如左图中的UILabel。

    对于以上的概念,我用一张图表来说明:






  • 0 0
    原创粉丝点击