在ARC下,IBOutlets应该强还是弱?


551

我正在使用ARC专为iOS 5开发。是否应IBOutlets到UIViewS(和子类)是strongweak

以下:

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

将摆脱所有这些:

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

这样做有什么问题吗?strong从“接口生成器”编辑器直接连接到标题时,模板使用的是自动生成的属性,但是为什么?在UIViewController已经有一个strong到其基准view保留其子视图。


11
注意,IBOutletCollection()不能为weak,否则返回为nil
哦,

通过接口构建器创建IBOutlet时,Xcode 8.2.1使用弱函数。但是,关于SO的许多答案建议使用强。
neoneye

1
@neoneye我刚尝试使用xcode 8.3.2从情节提要拖动到快速文件,它默认为strong
CupawnTae17年

Answers:


252

苹果公司目前建议的最佳做法是使IBOutlets 坚固,除非特别需要弱化以避免保留周期。正如Johannes所述,在WWDC 2015的“在Interface Builder中实现UI设计”会议中对此进行了评论,其中苹果工程师说:

我要指出的最后一个选项是存储类型,可以是强类型或弱类型。通常,您应该使出口坚固,尤其是当您将出口连接到子视图或约束时,视图层次结构并不总是会保留该出口。真正需要弱化出口的唯一情况是,如果您有一个自定义视图,该视图引用了备份视图层次结构的内容,通常不建议这样做。

我在Twitter上向IB团队的一名工程师询问了此问题,他确认将默认设置为“ 强”,并且正在更新开发人员文档。

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


33
这是真的吗?或者答案超过300的答案是正确的?我注意到当您从情节
提要

4
拥有400票以上的选票是正确的,但已经过时了。由于未调用iOS 6 viewDidUnload,因此插座薄弱无益。
kjam

7
@kjam有好处。首先,最重要的是,您不应强烈引用未创建的内容。其次,性能提升可忽略不计。不要仅仅因为某个人甚至是一个位置好的人就说这快10微秒,就违反了编程的最佳实践。代码意图明确,请勿尝试发挥优化编译器的作用。仅在特定情况下测量出性能问题时,才对性能进行编码。
卡梅隆·洛厄尔·帕尔默

5
让我不同意你的看法。在Objective-C中,“始终强烈引用未创建的内容”。这就是为什么有引用计数,而不是单个所有者的原因。您是否有备份该建议的参考?您能列举一下网点薄弱的其他好处吗?
kjam


450

警告,过时的答案:根据WWDC 2015,此答案不是最新的,有关正确答案,请参阅上面的公认答案(丹尼尔·霍尔)。该答案将留作记录。


来自开发人员库的摘要:

从实际的角度来看,在iOS和OS X中,出口应定义为已声明的属性。出口通常应该较弱,但从文件所有者到笔尖文件(或iOS中的情节提要场景)中顶级对象的出口则应较坚固。因此,默认情况下,您创建的插座通常较弱,因为:

  • 您为视图控制器的子视图或窗口控制器的窗口的子视图创建的出口是对象之间的任意引用,并不表示所有权。

  • 强势出口经常由框架类指定(例如,UIViewController的视图出口或NSWindowController的窗口出口)。

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;

10
您是如何获得“开发人员库”链接的,以跳转到Apple文档页面的特定部分?每当我链接到Apple文档时,它总是链接到页面顶部(即使感兴趣的内容位于页面的中间)。谢谢。
bearMountain 2011年

68
我从左侧的导航窗格复制了链接。:D
Alexsander Akers

27
“除了从文件所有者到笔尖文件(或在iOS中为演示图板场景)中的顶级对象的那些对象之外”是什么意思?
Van Du Tran 2012年

16
@VanDuTran-它表示NIB中处于根级别的对象,即说您在其中实例化了另一个视图,该视图不直接是主视图的子视图,那么它需要具有强大的引用。
mattjgalloway 2012年

6
最高级别意味着当您查看笔尖时,该对象将出现在左侧列表中。几乎所有笔尖都具有UIView-这可能是唯一的顶级对象。如果添加其他项目,并且它们显示在列表中,则它们是“顶级对象”
David H

50

尽管文档建议weak为子视图使用on属性,但自iOS 6起,似乎可以使用strong(默认所有权限定符)代替。这是由于UIViewController视图不再卸载而引起的。

  • 在iOS 6之前,如果您保持指向控制器视图子视图的强链接,如果视图控制器的主视图被卸载,则只要视图控制器在周围,这些视图就会保留在子视图中。
  • 从iOS 6开始,视图不再被卸载,而是被加载一次,然后只要它们的控制器在那里就一直存在。因此,强大的属性无关紧要。它们也不会创建强参考周期,因为它们指向强参考图。

也就是说,我在使用之间感到恐惧

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

@property (nonatomic) IBOutlet UIButton *button;

在iOS 6及更高版本中:

  • weak清楚地使用表示控制器不需要该按钮的所有权。

  • 但是,weak在不卸载视图的情况下,忽略iOS 6不会造成伤害,而且操作更短。可能有人指出它也更快,但是我还没有遇到因为weak IBOutlets 太慢的应用程序。

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

底线:从iOS 6开始,只要我们不使用视图卸载功能,我们就不会再出错。参加聚会的时间。;)


没错,但是您可能仍然想自己卸载视图。在这种情况下,您必须将所有网点nil手动设置。
hypercrypt

PS:weak在ARM64中便宜很多:D
hypercrypt

没错,如果您实现视图卸载,则可以使用weak属性或__weak实例变量。我只想指出,这里出错的可能性较小。至于weak在arm64上更便宜,我什至没有在armv7上看到weak IBOutlets 的实际性能问题。:)
Tammo Freese

在这种情况下,strong也是有道理的。strong仅当使用视图卸载时才有害–但是这些天是谁?:)
Tammo Freese 2014年

2
@Rocotilos第一部iPhone的RAM非常有限。如果我没记错的话,是128 MB,剩余的10 MB用于活动应用程序。较小的内存占用至关重要,因此需要卸载视图。随着我们现在拥有越来越多的RAM以及Apple在iOS 6中优化了UIViews,这种情况发生了变化,因此在出现内存警告时,无需卸载视图就可以释放很多内存。
Tammo Freese

34

我认为没有任何问题。在ARC之前,我一直制作IBOutlets assign,因为它们已被其超级视图保留。如果您制作了它们weak,就不必像您指出的那样在viewDidUnload中删除它们。

一个警告:您可以在ARC项目中支持iOS 4.x,但如果不能,则不能使用weak,因此必须将它们制作assign成这种情况,在这种情况下,您仍然想引用其中的内容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];
}

我发现,如果我在适当的时间取消了该视图并且没有将self.zip保留为n viewDidUnload,则委托回调可能会在self.zip.text上引发错误的访问异常。


4
我的理解也weak就是不需要删除属性viewDidUnload。但是,为什么苹果公司用于创建网点的模板包含一个[self setMySubview:nil]
Yang Meyer 2012年

3
在现实世界中,是否存在对您的IBOutlet使用坚固/保持型会引起问题的情况?还是仅仅是多余的保留,这意味着不良的编码风格,但不会影响您的代码?
Enzo Tran 2013年

1
有多余的保留吗?如果有额外的保留,这将导致无法正确计数,因此,由于保留数上有额外的保留,因此不会尽快释放它。
karlbecker_com 2014年

25

IBOutlet出于性能原因,应该坚固。请参阅情节提要参考,Strong IBOutlet,iOS 9中的场景扩展坞

如本段所述,视图控制器视图的子视图的出口可能很弱,因为这些子视图已经由nib文件的顶级对象拥有。但是,当将插座定义为弱指针并设置了指针时,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 在Interface Builder中实现UI设计,则建议使用(抄本来自http://asciiwwdc.com/2015/sessions/407

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

通常,您应该使出口坚固,尤其是当您将出口连接到子视图或视图层次结构并不总是保留的约束时。

真正需要弱化出口的唯一情况是,如果您有一个自定义视图,该视图引用了备份视图层次结构的内容,通常不建议这样做。

因此,我将选择“ strong”,然后单击“ connect”,这将生成我的插座。


1
很好的答案,解释了真正的原因
micnguyen

很好,但是我看到的所有泄漏都来自情节提要中实现的手势识别器。
thibaut noah's

1
我听不懂这句话。“唯一真正需要弱化出口的情况是,如果您有一个自定义视图,该视图引用了备份视图层次结构的内容,通常不建议这样做。” 有什么例子吗?
user1872384 '19

我计算了弱和强所需的deinit时间,它是完全相同的。
touti

但是情况更是如此。弱引用更快。
sumsumsign

20

在iOS开发中,NIB加载与Mac开发略有不同。

在Mac开发中,IBOutlet通常是一个较弱的参考:如果您有NSViewController的子类,则仅保留顶层视图,并且在取消分配控制器时,将自动释放其所有子视图和出口。

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

在Big Nerd Ranch的这篇文章中,他们涵盖了这个主题,并解释了为什么在IBOutlet中使用强引用不是一个好选择(即使Apple在这种情况下建议这样做)。


16
它说明了2009年的情况。使用ARC,情况发生了很大变化。
达菲德·威廉姆斯

1
:(Big Nerd Ranch链接已死……但我真的需要阅读它。有人知道该帖子的更多详细信息,所以我可以找到它?
Motti Shneor 2014年

@MottiShneor不用担心,这没什么大不了的,因为该链接大约是在ARC之前的时间出现的,不再相关。
谢尔盖·格里斯乔夫

18

我想在这里指出一件事,那就是,尽管苹果工程师在自己的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_Controller

就像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-Do

所有这些都针对iOS 9进行了全面更新,并且都使用弱电插座。由此我们可以了解到A.问题并不像某些人说的那么简单。B.苹果反复改变主意,C。您可以使用任何让自己开心的东西:)

特别感谢Paul Hudson(www.hackingwithsift.com的作者)给了我澄清,并提供了此答案的参考。

我希望这可以更好地阐明这个主题!

照顾自己。


我已经检查了一段时间,还没有找到任何具体的答案。由于上面的链接表明两者都很好,并且通常与Xcode的自动建议一起使用。
subin272 '19



5

看起来这些年来已经发生了一些变化,现在苹果建议一般使用强壮的。WWDC会议上的证据在会话407-在Interface Builder中实现UI设计,开始于32:30。他说的话我的笔记是(几乎,如果不完全是引用他的话):

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

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

在其他方面,只要我们的某些自定义视图不会在视图层次结构中的某些视图向上创建保留周期,它现在就应该总是很强大。

编辑:

有些人可能会问这个问题。用强引用保留它并不会创建保留周期,因为根视图控制器和拥有视图会保留对它的引用?还是为什么发生了这种变化?我认为答案是在本演讲的早期,当时他们描述了笔尖是如何通过xib创建的。为VC和视图创建了一个单独的笔尖。我认为这可能是他们更改建议的原因。从苹果那里得到更深层次的解释仍然是很好的。


4

我认为最重要的信息是:xib中的元素自动出现在视图的子视图中。子视图是NSArray。NSArray拥有它的元素。等对他们有很强的指导意义。因此,在大多数情况下,您不想创建另一个强指针(IBOutlet)

借助ARC,您无需执行任何操作 viewDidUnload

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.