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

hyp*_*ypt 542 cocoa-touch objective-c interface-builder ios automatic-ref-counting

我正在使用ARC专门为iOS 5开发.应该IBOutlets UIView(和子类)是strongweak

下列:

@property (nonatomic, weak) IBOutlet UIButton *button;
Run Code Online (Sandbox Code Playgroud)

将摆脱所有这一切:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这样做有什么问题吗?模板正在使用strong,当从"Interface Builder"编辑器直接连接到标题时创建的自动生成属性,但为什么?在UIViewController已经有一个strong到其基准view保留其子视图.

Ale*_*ers 448

警告,已解答答案:根据WWDC 2015,此答案不是最新的,因为正确的答案参考上面接受的答案(Daniel Hall).这个答案将留作记录.


开发人员库中总结:

从实际角度来看,在iOS和OS X中,出口应该被定义为声明的属性.Outlets通常应该是弱的,除了那些从File的Owner到nib文件中的顶级对象(或者在iOS中,故事板场景中)应该很强大.因此,默认情况下,您创建的出口通常较弱,因为:

  • 您创建的出口(例如,视图控制器视图或窗口控制器窗口的子视图)是不暗示所有权的对象之间的任意引用.

  • 强大的出口通常由框架类指定(例如,UIViewController的视图出口或NSWindowController的窗口出口).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    
    Run Code Online (Sandbox Code Playgroud)

  • 我从左侧导航窗格中复制了链接.:d (68认同)
  • "从文件所有者到nib文件中的顶级对象(或iOS中,故事板场景中的顶级对象)"的意思是什么? (27认同)
  • @VanDuTran - 它意味着NIB中的对象位于根级别,即假设您在那里实例化了另一个视图,该视图不直接是主视图的子视图,那么它需要具有强引用. (16认同)
  • 你是如何获得"开发者库"链接跳转到苹果文档页面的特定部分的?每当我链接到苹果文档时,它总是链接到页面顶部(即使感兴趣的内容在页面的中间).谢谢. (10认同)
  • 顶级意味着当您查看笔尖时,对象将显示在左侧列表中.几乎所有的笔尖都有UIView - 这可能是唯一的顶级对象.如果您添加其他项目,并且它们显示在列表中,则它们是"顶级对象" (6认同)
  • 不再是真的,应该更新 (5认同)
  • @jowie`unsafe_unretained`就是如果你的项目针对的是一个早于5的iOS版本但仍然支持ARC的产品.如果它只针对5或者上升,那么它会给你"弱". (2认同)
  • 是否有任何真实世界的情况,使用强力/保留您的IBOutlet可能会导致问题?或者它只是一个冗余保留,这意味着不良的编码风格,但不会影响您的代码? (2认同)

Dan*_*all 236

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

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

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

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

  • 这是真的还是300+ upvotes正确答案的答案?我注意到,当您从故事板按Ctrl键拖动到.h时,InterfaceBuilder默认使用弱 (31认同)
  • @kjam有好处.首先,你不应该强烈提及你没有创造的东西.其次,性能提升可以忽略不计.不要违反编程中的最佳实践,因为有些人,即使是一个安排得很好的人,也说这个速度快了10微秒.代码明确意图,不要尝试发挥优化编译器.只有在特定情况下测量成为问题时的性能代码. (5认同)
  • 让我不同意你的看法.在Objective-C中始终发生'强烈引用你没有创建的东西'.这就是为什么有一个参考**计数**,而不是一个单一的所有者.您是否有任何提及备份此建议的内容?你能列出弱电网的其他好处吗? (5认同)
  • 拥有400多张选票的人是正确的,但过时了.由于iOS 6的viewDidUnload没有被调用,所以没有任何好处,因为有弱的出口. (4认同)
  • 以下是答案中提到的WWDC视频https://developer.apple.com/videos/play/wwdc2015/407/?time=1946 (4认同)
  • "这真的是真的,或者是[更多]赞成正确的答案吗?"当这个答案引用苹果工程师作为其来源时,这不是一个真正有效的问题.根据定义,您可以考虑Apple对Apple的API使用情况的正确看法. (2认同)
  • @ChrisHanson 差不多三年后,苹果的文档仍然声明它们应该很弱,但“除了从文件所有者到顶级对象的那些”。那么我们应该相信文档还是工程师呢? (2认同)
  • 这个答案只是说"做这个"并没有解释任何事情.如果我需要弱引用_sometimes_,而在其他时候它并不重要,为什么我会使用强引用?我不在乎谁发布这些信息,必须加以解释,否则只是某人的意见. (2认同)
  • 奇怪的是,如果不建议这样做,则按住 Ctrl 键拖动到 .h 文件仍然会产生弱信号(从 XCode 10 开始)。不过我对此没有任何问题。一切正常。这很令人困惑。 (2认同)
  • 我不同意。`IBOutlets` 应该被声明为弱。创建强引用不会允许视图解除分配。这是一个很好的做法,但对于那些知道自己在做什么的人来说,这可能是个人偏好。例如,在控制器中创建对嵌套子视图的强引用并删除其父视图不会释放内存,因为它很强。更好的是让 outlet 成为可选的,而不是隐式解包以避免崩溃。 (2认同)

Tam*_*ese 48

虽然文档建议weak在子视图的属性上使用,但是从iOS 6开始,它似乎可以使用strong(默认的所有权限定符).这是因为UIViewController视图中的更改不再被卸载.

  • 在iOS 6之前,如果您保持与控制器视图的子视图的强大链接,如果视图控制器的主视图被卸载,只要视图控制器在周围,那些将保留在子视图上.
  • 从iOS 6开始,视图不再被卸载,而是加载一次,然后只要控制器在那里就可以继续.如此强大的属性无关紧要.他们也不会创建强大的参考周期,因为他们指出了强大的参考图.

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

@property (nonatomic, weak) IBOutlet UIButton *button;
Run Code Online (Sandbox Code Playgroud)

@property (nonatomic) IBOutlet UIButton *button;
Run Code Online (Sandbox Code Playgroud)

在iOS 6及之后:

  • 使用weak明确指出,控制器不希望按钮的所有权.

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

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

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

  • @Rocotilos 第一款 iPhone 的内存非常有限。如果我没记错的话,128 MB,为活动应用程序留下大约 10 MB。拥有小的内存占用是至关重要的,因此有视图卸载。随着我们现在拥有越来越多的 RAM,并且 Apple 优化了 iOS 6 中的 UIViews,这种情况发生了变化,因此在出现内存警告时,可以在不卸载视图的情况下释放大量内存。 (2认同)

Chr*_*lay 34

我没有看到任何问题.在ARC之前,我总是制作我的IBOutlets assign,因为他们已经被他们的超级视图保留了下来.如果你制作它们weak,你应该不必在viewDidUnload中将它们取出,正如你所指出的那样.

一个警告:您可以在ARC项目中支持iOS 4.x,但如果您这样做,则无法使用weak,因此您必须制作它们assign,在这种情况下,您仍然希望将引用设置为n viewDidUnload来避免一个悬垂的指针.这是我经历过的悬空指针错误的一个例子:

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];
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我的理解是``weak`属性不需要在`viewDidUnload`中填充.但是为什么Apple用于创建出口的模板包含一个`[self setMySubview:nil]`? (4认同)
  • 是否有任何真实世界的情况,使用强力/保留您的IBOutlet可能会导致问题?或者它只是一个冗余保留,这意味着不良的编码风格,但不会影响您的代码? (3认同)

onm*_*133 23

IBOutlet出于性能原因,应该很强大.请参阅iOS 9中的Storyboard Reference,Strong IBOutlet,Scene Dock

如本段所述,视图控制器视图子视图的出口可能较弱,因为这些子视图已由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 实现UI设计,它建议(来自http://asciiwwdc.com/2015/sessions/407的成绩单)

我要指出的最后一个选项是存储类型,它可以是强也可以是弱.

通常,您应该使插座变得强大,尤其是在将插座连接到子视图或者视图层次结构并不总是保留的约束时.

唯一真正需要让出口变弱的一个方法是,如果你有一个自定义视图引用视图层次结构的某些内容,一般不建议这样做.

所以我要选择强,我会点击连接,这将生成我的插座.

  • 我仍然弱势默认(Xcode 8) (2认同)

Giu*_*ppe 20

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

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

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

在Big Nerd Ranch的这篇文章中,他们讨论了这个主题,并解释了为什么在IBOutlet中使用强引用不是一个好的选择(即使在这种情况下Apple推荐).

  • 它解释了它在2009年.随着ARC,这已经发生了重大变化. (16认同)

sye*_*dfa 18

我想在此指出一点,那就是,尽管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

与Core Location示例一样: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的作者)给我的澄清,以及对这个答案的参考.

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

照顾自己.


Joh*_*nes 9

从WWDC 2015开始,有一个关于在Interface Builder中实现UI设计的会议.在大约32分钟的时间里,他说你总是想让自己变得@IBOutlet 强壮.


lan*_*rey 6

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

  • 为什么不"复制",因为它是一个"NSArray"? (3认同)

Jul*_*ról 5

看起来这些年来发生了一些变化,现在Apple建议使用强大的功能.他们的WWDC会议上的证据是在会话407中 - 在Interface Builder中实现UI设计并在32:30开始.我所说的是(几乎,如果不完全是,引用他):

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

  • 在创建自定义视图时可能需要弱插座连接,该视图对视图层次结构中备份的内容有一些参考,通常不建议这样做

在其他病房中,只要我们的某些自定义视图没有创建保留周期,并且视图层次结构中有一些视图,它就应该总是很强大

编辑:

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