565

我正在使用 ARC 专门为 iOS 5 开发。IBOutlets to UIViews(和子类)应该是orstrongweak

以下:

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

将摆脱所有这些:

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

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

4

11 回答 11

454

警告,过时的答案:根据 WWDC 2015,此答案不是最新的,正确答案请参阅上面接受的答案(Daniel Hall)。这个答案将留作记录。


开发者库中总结:

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

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

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

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    
于 2011-10-11T16:10:20.873 回答
275

Apple 当前推荐的最佳实践是让 IBOutlets,除非特别需要来避免保留周期。正如 Johannes 上面提到的,这在 WWDC 2015 的“在 Interface Builder 中实现 UI 设计”会话中得到了评论,其中一位 Apple 工程师说:

我要指出的最后一个选项是存储类型,它可以是强的,也可以是弱的。一般来说,您应该使您的出口变得强大,特别是如果您将出口连接到子视图或视图层次结构并不总是保留的约束。唯一真正需要使插座变弱的情况是,如果您有一个自定义视图引用了视图层次结构中的某些内容,并且通常不推荐这样做。

我在 Twitter 上向 IB 团队的一位工程师询问了这个问题,他确认strong应该是默认设置,并且正在更新开发人员文档。

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

于 2015-07-14T00:59:00.750 回答
51

虽然文档建议对子视图使用on 属性,但从 iOS 6 开始,使用(默认所有权限定符)weak似乎很好。strongUIViewController这是由于不再卸载视图的变化引起的。

  • 在 iOS 6 之前,如果你保持到控制器视图的子视图的强链接,如果视图控制器的主视图被卸载,只要视图控制器在附近,它们就会保留子视图。
  • 从 iOS 6 开始,视图不再被卸载,而是加载一次,然后只要它们的控制器存在就一直存在。所以强大的属性并不重要。它们也不会创建强参考循环,因为它们指向强参考图。

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

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

@property (nonatomic) IBOutlet UIButton *button;

在 iOS 6 及更高版本中:

  • Usingweak清楚地表明控制器不想要按钮的所有权。

  • 但是在没有视图卸载的 iOS 6 中省略weak并没有什么坏处,而且更短。有人可能会指出这也更快,但我还没有遇到过因为weak IBOutlets.

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

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

于 2013-12-10T21:54:30.393 回答
34

我看不出有什么问题。Pre-ARC,我总是制作我的 IBOutlets assign,因为它们已经被他们的超级视图保留了。weak正如您所指出的,如果您制作它们,则不必在 viewDidUnload 中将它们归零。

一个警告:您可以在 ARC 项目中支持 iOS 4.x,但如果这样做,您将无法使用weak,因此您必须制作它们assign,在这种情况下,您仍然需要 nilviewDidUnload来避免引用一个悬空指针。这是我遇到的一个悬空指针错误的示例:

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 中设置 nil viewDidUnload,委托回调可能会在 self.zip.text 上抛出一个错误的访问异常。

于 2011-10-07T22:34:34.493 回答
25

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 session 407 Implementing UI Designs in Interface Builder,它建议(来自http://asciiwwdc.com/2015/sessions/407的成绩单)

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

一般来说,您应该使您的插座变得强大,特别是如果您将插座连接到子视图或视图层次结构并不总是保留的约束。

唯一真正需要使插座变弱的情况是,如果您有一个自定义视图引用了视图层次结构中的某些内容,并且通常不建议这样做。

所以我要选择 strong 并且我会点击 connect 这将生成我的出口。

于 2015-11-04T04:27:16.930 回答
20

In iOS development NIB loading is a little bit different from Mac development.

In Mac development an IBOutlet is usually a weak reference: if you have a subclass of NSViewController only the top-level view will be retained and when you dealloc the controller all its subviews and outlets are freed automatically.

UiViewController use Key Value Coding to set the outlets using strong references. So when you dealloc your UIViewController, the top view will automatically deallocated, but you must also deallocate all its outlets in the dealloc method.

In this post from the Big Nerd Ranch, they cover this topic and also explain why using a strong reference in IBOutlet is not a good choice (even if it is recommended by Apple in this case).

于 2011-10-10T16:02:21.260 回答
18

我想在这里指出一件事,那就是,尽管 Apple 工程师在他们自己的 WWDC 2015 视频中已经说过:

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

苹果在这个问题上不断改变主意,这告诉我们这个问题没有唯一的正确答案。为了表明即使是苹果工程师在这个问题上也存在分歧,看看苹果最近的示例代码,你会看到有些人使用弱,有些人没有。

此 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-HMatalog_Homes_Action_Sets_ActionSetViewController_swift-DontLinkElementID_23

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

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

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

小心。

于 2016-05-05T21:25:41.610 回答
10

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

于 2015-07-09T13:11:03.050 回答
6

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

于 2014-03-25T13:02:53.187 回答
5

多年来,似乎有些东西发生了变化,现在 Apple 建议总体上使用 strong 。他们的 WWDC 会议的证据在会议 407 - 在 Interface Builder 中实现 UI 设计,从 32:30 开始。我对他所说的话的注释是(几乎,如果不完全是引用他的话):

  • 一般来说,出口连接应该是强的,尤其是当我们连接视图层次结构并不总是保留的子视图或约束时

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

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

编辑 :

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

于 2015-08-07T08:28:38.960 回答
4

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

使用 ARC,您无需在viewDidUnload

于 2012-12-04T14:49:28.657 回答