首页 文章

如果IBOutlets在ARC下是强还是弱?

提问于
浏览
521

我正在使用ARC专门为iOS 5开发 . IBOutlet s(和子类)是 strong 还是 weak

下列:

@property (nonatomic, weak) IBOutlet UIButton *button;

将摆脱所有这一切:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

这样做有什么问题吗?模板使用 strong ,直接连接到'Interface Builder'编辑器的 Headers 时创建的自动生成属性,但为什么? UIViewController 已经 strong 对其 view 的引用保留了其子视图 .

11 回答

  • 6

    Apple目前推荐的最佳做法是IBOutlets强劲,除非特别需要弱以避免保留周期 . 正如Johannes在上面提到的那样,在2015年WWDC的"Implementing UI Designs in Interface Builder" Session 上对此进行了评论,Apple工程师说:

    我想指出的最后一个选项是存储类型,它可以是强也可以是弱 . 通常,您应该使插座变得强大,尤其是在将插座连接到子视图或者视图层次结构并不总是保留的约束时 . 唯一真正需要让出口变弱的一个方法是,如果你有一个自定义视图引用视图层次结构的某些内容,一般不建议这样做 .

    我在Twitter上向IB团队的一位工程师询问了这一点,他确认强者应该是默认的,并且开发人员文档正在更新 .

    https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104

  • 4

    WARNING, OUTDATED ANSWER :根据WWDC 2015,此答案不是最新的,正确答案请参阅上面的accepted answer(Daniel Hall) . 这个答案将留作记录 .


    摘自developer library

    从实际角度来看,在iOS和OS X中,出口应该被定义为声明的属性 . Outlets通常应该是弱的,除了那些从File的Owner到nib文件中的顶级对象(或者在iOS中,故事板场景中)应该很强大 . 因此,默认情况下,您创建的出口通常较弱,因为:您创建的出口(例如,视图控制器视图或窗口控制器窗口的子视图)是不暗示所有权的对象之间的任意引用 . 强大的出口通常由框架类指定(例如,UIViewController的视图出口或NSWindowController的窗口出口) . @property(弱)IBOutlet MyView * viewContainerSubview;
    @property(strong)IBOutlet MyOtherClass * topLevelObject;

  • 9

    我认为最重要的信息是:xib中的元素自动出现在视图的子视图中 . 子视图是NSArray . NSArray拥有它的元素 . 等等有强烈的指针 . 所以在大多数情况下你不想创建另一个强指针(IBOutlet)

    使用ARC,您无需在 viewDidUnload 中执行任何操作

  • 16

    While the documentation recommends using weak on properties for subviews, since iOS 6 it seems to be fine to use strong (the default ownership qualifier) instead. 这是由于 UIViewController 中的更改导致视图不再被卸载 .

    • 在iOS 6之前,如果你保持强大的链接到控制器的子视图's view around, if the view controller'的主视图被卸载,只要视图控制器在周围,那些将保留在子视图上 .

    • 从iOS 6开始,视图不再被卸载,只是加载了一次,然后只要控制器在那里就停留在那里 . 如此强大的属性赢得了创建强大的参考周期,因为他们指出了强大的参考图 .

    也就是说,我在使用之间挣扎

    @property (nonatomic, weak) IBOutlet UIButton *button;
    

    @property (nonatomic) IBOutlet UIButton *button;
    

    在iOS 6及之后:

    • 使用 weak 明确指出控制器不想要按钮的所有权 .

    • 但是在没有视图卸载的情况下省略 weak 在iOS 6中没有受到伤害,并且更短 . 有些人可能会指出它也更快,但我还没有遇到一个因为 weak IBOutlet 而太慢的应用程序 .

    • 不使用 weak 可能会被视为错误 .

    结论:从iOS 6开始,只要我们不使用视图卸载,我们就不会再出错了 . 聚会的时间 . ;)

  • 445

    我总是让我的IBOutlets assign ,因为他们已经被他们的超级视图保留了 . 如果你将它们设为 weak ,你应该不必在viewDidUnload中将它们取出,正如你所指出的那样 .

    一个警告:您可以在ARC项目中支持iOS 4.x,但是如果您这样做,则不能使用 weak ,因此您必须将它们设为 assign ,在这种情况下,您仍然希望将引用设为nil viewDidUnload 避免悬空指针 . 在这里's an example of a dangling pointer bug I'经历过:

    UIViewController有一个UITextField用于邮政编码 . 它使用CLLocationManager反转地理编码用户的位置并设置邮政编码 . 这是委托回调:

    -(void)locationManager:(CLLocationManager *)manager
       didUpdateToLocation:(CLLocation *)newLocation
              fromLocation:(CLLocation *)oldLocation {
        Class geocoderClass = NSClassFromString(@"CLGeocoder");
        if (geocoderClass && IsEmpty(self.zip.text)) {
            id geocoder = [[geocoderClass alloc] init];
            [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
                if (self.zip && IsEmpty(self.zip.text)) {
                    self.zip.text = [[placemarks objectAtIndex:0] postalCode];
                }
            }];    
        }
        [self.locationManager stopUpdatingLocation];
    }
    

    我发现如果我在正确的时间解散了这个视图并且在 viewDidUnload 中没有nil self.zip,则委托回调可能会在self.zip.text上抛出一个错误的访问异常 .

  • 46

    在iOS开发NIB中加载与Mac开发有点不同 .

    在Mac开发中,IBOutlet通常是一个弱引用:如果你有一个NSViewController的子类,只保留顶级视图,当你解除控制器时,它的所有子视图和出口都会被自动释放 .

    UiViewController使用键值编码来使用强引用设置出口 . 因此,当您释放UIViewController时,顶视图将自动解除分配,但您还必须在dealloc方法中释放其所有出口 .

    In this post from the Big Nerd Ranch,它们涵盖了这个主题,并解释了为什么在IBOutlet中使用强引用不是一个好的选择(即使在这种情况下Apple推荐) .

  • 201

    出于性能原因, IBOutlet 应该很强大 . 见Storyboard Reference, Strong IBOutlet, Scene Dock in iOS 9

    如本段所述,视图控制器视图子视图的出口可能较弱,因为这些子视图已由nib文件的顶级对象拥有 . 但是,当Outlet被定义为弱指针并且指针被设置时,ARC调用运行时函数:id objc_storeWeak(id * object,id value);这会使用对象值作为键将指针(对象)添加到表中 . 该表称为弱表 . ARC使用此表来存储应用程序的所有弱指针 . 现在,当取消分配对象值时,ARC将遍历弱表并将弱引用设置为nil . 或者,ARC可以调用:void objc_destroyWeak(id * object)然后,该对象被取消注册并再次调用objc_destroyWeak:objc_storeWeak(id * object,nil)与弱引用相关联的簿记可能需要2-3倍的时间 . 发布一个强大的参考 . 因此,弱引用会为运行时引入开销,您可以通过简单地将出口定义为强大来避免 .

    从Xcode 7开始,它建议 strong

    如果您观看WWDC 2015会话407 Implementing UI Designs in Interface Builder,它建议(成绩单来自http://asciiwwdc.com/2015/sessions/407

    我想指出的最后一个选项是存储类型,它可以是强也可以是弱 . 通常,您应该使插座变得强大,尤其是在将插座连接到子视图或者视图层次结构并不总是保留的约束时 . 唯一真正需要让出口变弱的一个方法是,如果你有一个自定义视图引用视图层次结构的某些内容,一般不建议这样做 . 所以我要选择强,我会点击连接,这将生成我的插座 .

  • 34

    我想在此指出一点,那就是,尽管Apple工程师在他们自己的WWDC 2015视频中已经说明了这一点:

    https://developer.apple.com/videos/play/wwdc2015/407/

    Apple一直在改变主题,这告诉我们这个问题没有一个正确答案 . 为了表明即使苹果公司的工程师在这个问题上存在分歧,看看Apple的最新示例代码,你会发现有些人使用弱,有些则不然 .

    此Apple Pay示例使用弱:https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_swift-DontLinkElementID_8

    就像这个画中画示例一样:https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID_4

    和Lister示例一样:https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

    与核心位置示例一样:https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElementID_6

    与视图控制器预览示例一样:https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift-DontLinkElementID_5

    与HomeKit示例一样:https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HMCatalog_Homes_Action_Sets_ActionSetViewController_swift-DontLinkElementID_23

    所有这些都是针对iOS 9完全更新的,并且都使用弱插座 . 从中我们了解到A.问题并不像有些人想象的那么简单 . B. Apple反复改变主意,C . 你可以用任何让你开心的东西:)

    特别感谢Paul Hudson(www.hackingwithsift.com的作者)给我的澄清,以及对这个答案的参考 .

    我希望这能更好地澄清这个主题!

    照顾自己 .

  • 5

    从WWDC 2015开始,有一个关于Implementing UI Designs in Interface Builder的 Session . 大约在32分钟左右,他说你总是想让自己成为 @IBOutlet strong .

  • 11

    请注意, IBOutletCollection 应为 @property (strong, nonatomic) .

  • 20

    看起来这些年来发生了一些变化,现在Apple建议使用强大的功能 . WWDC Session 的证据在session 407 - Implementing UI Designs in Interface Builder,从32:30开始 . 我所说的是(几乎,如果不完全是,引用他):

    • 出口连接通常应该很强,特别是如果我们连接的视图层次结构并不总是保留的子视图或约束

    创建自定义视图时可能需要

    • 弱插座连接,该视图对视图层次结构中备份的内容有一些参考,通常不建议这样做

    在其他病房,它应该永远现在很强大,只要我们的某些自定义视图不会在视图层次结构中创建一些保留周期

    EDIT :

    有些人可能会问这个问题 . 使用强引用保留它不会创建保留周期,因为根视图控制器和拥有视图会保留对它的引用吗?或者为什么发生了变化?我认为当他们描述如何从xib创建笔尖时,答案在本次演讲中更早 . 为VC和视图创建了一个单独的nib . 我认为这可能是他们改变建议的原因 . 从Apple获得更深入的解释仍然是一件好事 .

相关问题