我使用ARC专门为iOS 5开发游戏。iboutlet到UIViews(和子类)是强还是弱?

以下几点:

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

就能摆脱这一切

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

做这个有什么问题吗?模板使用强,当从“接口生成器”编辑器直接连接到头部时,自动生成的属性创建,但为什么?UIViewController已经有一个强引用到它的视图,它保留了它的子视图。


当前回答

我想在这里指出一件事,那就是,尽管苹果工程师在他们自己的2015年全球开发者大会视频中说过:

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

苹果公司在这个问题上一直在改变主意,这告诉我们这个问题没有唯一的正确答案。为了证明就连苹果的工程师在这个问题上也存在分歧,看看苹果最近的一份报告就知道了 示例代码,你会看到有些人使用weak,有些人不用。

这个Apple Pay的例子使用了weak: 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完全更新的,并且都使用弱outlet。这个问题并不像有些人想的那么简单。B.苹果一再改变主意,C.你可以用任何让你高兴的东西:)

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

我希望这能更好地阐明主题!

当心

其他回答

这几年似乎发生了一些变化,现在苹果建议一般使用strong。在他们的WWDC会议的证据是在会议407 -实现UI设计的界面生成器,并开始于32:30。我从他的话中摘录(如果不是完全引用的话,也几乎是引用了他的话):

Outlet连接通常应该是强的,特别是当我们连接的子视图或约束并不总是由 视图层次 在创建自定义视图时,可能需要弱出口连接,因为自定义视图引用了视图层次结构中的备份内容 一般来说不建议这样做

在其他方面,它应该总是强的只要我们的一些自定义视图没有与视图层次结构中的一些视图创建一个保留循环

编辑:

有些人可能会问这个问题。用强引用保存它是否不会创建一个保留循环作为根视图控制器而所属视图保留对它的引用?或者为什么会发生这种变化? 我想答案在这个演讲的前面,当他们描述如何从xib创建nib时。为VC和视图创建了一个单独的nib。我认为这可能是他们改变建议的原因。不过,如果能从苹果那里得到更深入的解释就好了。

我看不出有什么问题。在arc之前,我总是让IBOutlets赋值,因为它们已经被它们的父视图保留了。如果你把它们设为弱的,你不应该在viewDidUnload中清空它们,正如你指出的那样。

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

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];
}

我发现,如果我在正确的时间解散这个视图,没有nil self.zip在viewDidUnload,委托回调可以抛出一个糟糕的访问异常self.zip.text。

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

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

UiViewController使用键值编码来设置出口使用强引用。当你dealloc你的UIViewController时,顶视图会自动释放,但你也必须在dealloc方法中释放它所有的outlet。

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

我想在这里指出一件事,那就是,尽管苹果工程师在他们自己的2015年全球开发者大会视频中说过:

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

苹果公司在这个问题上一直在改变主意,这告诉我们这个问题没有唯一的正确答案。为了证明就连苹果的工程师在这个问题上也存在分歧,看看苹果最近的一份报告就知道了 示例代码,你会看到有些人使用weak,有些人不用。

这个Apple Pay的例子使用了weak: 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完全更新的,并且都使用弱outlet。这个问题并不像有些人想的那么简单。B.苹果一再改变主意,C.你可以用任何让你高兴的东西:)

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

我希望这能更好地阐明主题!

当心

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

在ios6之前,如果你保持强链接到控制器视图的子视图,如果视图控制器的主视图被卸载,只要视图控制器还在,那些就会保留子视图。 自iOS 6以来,视图不再被卸载,而是加载一次,然后只要它们的控制器还在,视图就会一直存在。所以强属性无关紧要。它们也不会创建强引用循环,因为它们向下指向强引用图。

也就是说,我在使用之间犹豫不决

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

and

@property (nonatomic) IBOutlet UIButton *button;

iOS 6及以后版本:

使用weak清楚地表明控制器不想要按钮的所有权。 但是省略weak在iOS 6中没有视图卸载,而且更短。有些人可能会指出,这也更快,但我还没有遇到过因为iboutlet太弱而太慢的应用程序。 不使用weak可能会被认为是错误。

底线:自从iOS 6以来,只要我们不使用视图卸载,我们就不会再犯这个错误了。聚会时间到了。;)