繁体   English   中英

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

[英]Should IBOutlets be strong or weak under ARC?

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

下列:

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

将摆脱所有这些:

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

这样做有什么问题吗? 从“接口生成器”编辑器直接连接到标题时,模板使用的功能很strong就像自动生成的属性一样,但是为什么? UIViewController已经对其view进行了strong引用,并保留了其子视图。

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


来自开发人员库的摘要:

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

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

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

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

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

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

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

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

尽管文档建议对子视图使用weak属性,但是从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造成伤害,并且更短。 有人可能会指出这也更快,但是由于IBOutlet weak ,我还没有遇到一个太慢的应用程序。

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

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

我认为没有任何问题。 在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];
}

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

出于性能原因, 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”,这将生成我的插座。

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

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

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

在Big Nerd Ranch的这篇文章中 ,他们涵盖了这个主题,并解释了为什么在IBOutlet中使用强引用不是一个好选择(即使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_Pre_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的作者)给了我澄清,并提供了此答案的参考。

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

照顾自己。

从WWDC 2015开始,有一个关于在Interface Builder中实现UI设计的会议。 他说,在32 @IBOutlet左右,您总是想使@IBOutlet 更强

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

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

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

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

在其他方面,只要我们的某些自定义视图没有在视图层次结构中使某些视图向上创建保留周期,它现在应该一直很强大

编辑:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM