在IB之下IBOutlets应该强大还是弱?

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

下列:

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

将摆脱所有这一切:

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

这样做有什么问题吗? 模板使用的strong与直接连接到“Interface Builder”编辑器中的标题时创build的自动生成的属性一样strong ,但是为什么? UIViewController已经有了strong引用,它保留了它的子视图。

目前推荐的苹果公司最佳做法是让IBOutlets变得强大,除非特别需要软件来避免保留周期。 正如Johannes上面提到的那样,在WWDC 2015的“在界面构build器中实现UIdevise”会话中对此进行了评论,苹果工程师说:

我想指出的最后一个选项是存储types,它可以是强大的,也可以是弱的。 一般来说,你应该让你的sockets强大,特别是如果你连接到一个子视图的出口或一个约束,并不总是会保留视图层次结构。 唯一一次你真的需要做一个sockets弱,如果你有一个自定义的视图引用备份的视图层次结构,一般不build议。

我在Twitter上向IT工程师询问了这个问题,他确认strong应该是默认设置,开发人员文档正在更新。

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

警告,错误答案 :根据WWDC 2015,此答案不是最新的,因为正确答案请参阅上面接受的答案 (Daniel Hall)。 这个答案将留作logging。


从开发者库总结:

从实际的angular度来看,在iOS和OS X的网点应该被定义为声明属性。 除了从文件所有者到nib文件中的顶层对象(或者在iOS中,故事板场景),这些应该是强大的。 因此,您创build的sockets默认情况下通常很弱,因为:

  • 您创build的出口(例如,视图控制器的视图或窗口控制器的窗口的子视图)是对象间的任意引用,并不意味着拥有所有权。

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

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

尽pipe文档build议在子视图上使用weak属性,但是从iOS 6开始,使用strong (默认所有权限定符)代替它似乎很好。 这是由UIViewController中的更改造成的,视图不再被卸载。

  • 在iOS 6之前,如果你持有与控制器视图的子视图的强大链接,如果视图控制器的主视图被卸载,那么只要视图控制器在附近,这些视图就会保持子视图。
  • 从iOS 6开始,视图不再被卸载,只是加载一次,然后只要控制器在那里就停留。 如此强大的属性并不重要。 他们也不会创build强大的参考周期,因为他们指出强大的参考图。

这就是说,我在使用之间被撕裂

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

 @property (nonatomic) IBOutlet UIButton *button; 

在iOS 6和之后:

  • 使用weak清楚地表明控制器不需要button的所有权。

  • 但是忽略weak不会在iOS 6中看不到卸载,而且更短。 有些人可能会指出,这也是更快,但我还没有遇到一个应用程序,由于IBOutlet weak太慢了。

  • 不使用weak可能被认为是一个错误。

底线:从iOS 6开始,只要我们不使用视图卸载,我们就不会再犯这个错误了。 派对的时间。 ;)

我没有看到任何问题。 在ARC之前,我一直让我的IBOutlets assign ,因为他们已经保留了他们的超级视图。 如果你使它们变weak ,你不应该在viewDidUnload中将它们清零,正如你指出的那样。

一个告诫:你可以在ARC项目中支持iOS 4.x,但是如果你这样做,你不能使用weak ,所以你必须让它们assign ,在这种情况下,你仍然想要在参考viewDidUnload以避免悬挂指针。 下面是一个我经历过的悬挂指针错误的例子:

一个UIViewController有一个用于邮编的UITextField。 它使用CLLocationManager来反向地理编码用户的位置并设置邮政编码。 这是委托callback:

 -(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没有self.zip,委托callback会在self.zip.text上抛出一个错误的访问exception。

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

在Mac开发中,一个IBOutlet通常是一个弱引用:如果您有NSViewController的子类,则只保留顶层视图,当您释放控制器时,其所有子视图和sockets将自动释放。

UiViewController使用Key Value Coding来设置使用强引用的出口。 所以当你释放你的UIViewController时,顶视图会自动解除分配,但你也必须在dealloc方法中释放所有的sockets。

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

从WWDC 2015开始, 在Interface Builder中实现UIdevise 。 在32 @IBOutlet左右,他说你总是想让你的@IBOutlet 强大

由于性能原因, IBOutlet应该很强大。 请参阅iOS 9中的故事板参考,强大的IBOutlet,Scene Dock

如本段所述,视图控制器视图的子视图的出口可能很弱,因为这些子视图已经被nib文件的顶层对象所拥有。 但是,当Outlet被定义为弱指针并且指针被设置时,ARC调用运行时函数:

id objc_storeWeak(id *object, id value);

这使用对象值作为关键字将指针(对象)添加到表中。 这个表格被称为弱表。 ARC使用此表来存储应用程序的所有弱指针。 现在,当对象值被释放时,ARC将迭代弱表并将弱引用设置为零。 或者,ARC可以调用:

void objc_destroyWeak(id * object)

然后,该对象被取消注册,objc_destroyWeak再次调用:

objc_storeWeak(id *object, nil)

与强参考文献的发布相比,这种与弱参考文献相关的记帐时间可能会延长2-3倍。 所以,一个弱引用引入了运行时的开销,你可以通过简单的定义强点来避免这种开销。

从Xcode 7开始,这表明strong

我想指出的一点是,尽pipe苹果工程师们在WWDC 2015video中说过:

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

苹果一直在改变主意,这告诉我们这个问题没有单一的正确答案。 为了表明即使苹果公司的工程师也在这个问题上分道扬take,看一下苹果公司最近的样本代码,你会看到一些人使用弱,有些人不使用。

此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.苹果一再改变主意,C。你可以使用任何让你开心的事情:)

特别感谢Paul Hudson(www.hackingwithsift.com的作者)给了我这个答案的澄清和参考。

我希望这个主题澄清一点!

保重。

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

看起来这些年来一些东西已经发生了变化,现在苹果公司build议使用强大的一般。 WWDC会话中的证据在会话407 – 在Interface Builder中实现UIdevise,并在32:30开始。 我所说的是(几乎,如果不是完全的话,引用他的话):

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

  • 创build自定义视图时,可能需要弱连接器连接,这些自定义视图对视图层次结构中备份的内容有一定的引用,通常不build议这样做

在其他病房中,只要我们的某些自定义视图不能创build一个保留周期,并且在视图层次结构中有一些视图,就应该始终保持强大

编辑:

有人可能会问这个问题。 保持强引用不会创build一个保留循环,因为根视图控制器和拥有视图保持对它的引用? 或者为什么发生了变化? 我想这个问题的答案是在他们描述如何从xib创buildnib的时候。 有一个单独的笔尖创build一个VC和视图。 我想这可能是他们改变build议的原因。 不过,从苹果公司得到更深入的解释还是不错的。

我认为最重要的信息是:xib中的元素自动处于视图的子视图中。 子视图是NSArray。 NSArray拥有它的元素。 对他们有强烈的指点。 所以在大多数情况下,你不想创build另一个强指针(IBOutlet)

而用ARC你不需要在viewDidUnload做任何事情