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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7678469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:46:12  来源:igfitidea点击:

Should IBOutlets be strong or weak under ARC?

iosobjective-ccocoa-touchinterface-builderautomatic-ref-counting

提问by hypercrypt

I am developing exclusively for iOS 5 using ARC. Should IBOutlets to UIViews (and subclasses) be strongor weak?

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

The following:

下列:

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

Would get rid of all of this:

将摆脱所有这些:

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

Are there any problems doing this? The templates are using strongas are the automatically generated properties created when connecting directly to the header from the 'Interface Builder' editor, but why? The UIViewControlleralready has a strongreference to its viewwhich retains its subviews.

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

采纳答案by Daniel Hall

The current recommended best practice from Apple is for IBOutlets to be strongunless weakis specifically needed to avoid a retain cycle. As Johannes mentioned above, this was commented on in the "Implementing UI Designs in Interface Builder" session from WWDC 2015 where an Apple Engineer said:

Apple 目前推荐的最佳实践是 IBOutlets 是强的,除非特别需要以避免保留周期。正如上面提到的 Johannes,在 WWDC 2015 的“在界面生成器中实现 UI 设计”会议中对此进行了评论,其中一位 Apple 工程师说:

And the last option I want to point out is the storage type, which can either be strong or weak. In general you should make your outlet strong, especially if you are connecting an outlet to a subview or to a constraint that's not always going to be retained by the view hierarchy. The only time you really need to make an outlet weak is if you have a custom view that references something back up the view hierarchy and in general that's not recommended.

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

I asked about this on Twitter to an engineer on the IB team and he confirmed that strongshould be the default and that the developer docs are being updated.

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

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

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

回答by Alexsander Akers

WARNING, OUTDATED ANSWER: this answer is not up to date as per WWDC 2015, for the correct answer refer to the accepted answer(Daniel Hall) above. This answer will stay for record.

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



Summarized from the developer library:

开发者库总结:

From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File's Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because:

  • Outlets that you create to, for example, subviews of a view controller's view or a window controller's window, are arbitrary references between objects that do not imply ownership.

  • The strong outlets are frequently specified by framework classes (for example, UIViewController's view outlet, or NSWindowController's window outlet).

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

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

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

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

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

回答by Tammo Freese

While the documentation recommends using weakon properties for subviews, since iOS 6 it seems to be fine to use strong(the default ownership qualifier) instead.That's caused by the change in UIViewControllerthat views are not unloaded anymore.

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

  • Before iOS 6, if you kept strong links to subviews of the controller's view around, if the view controller's main view got unloaded, those would hold onto the subviews as long as the view controller is around.
  • Since iOS 6, views are not unloaded anymore, but loaded once and then stick around as long as their controller is there. So strong properties won't matter. They also won't create strong reference cycles, since they point down the strong reference graph.
  • 在 iOS 6 之前,如果您保持与控制器视图的子视图的强链接,如果视图控制器的主视图被卸载,只要视图控制器在附近,这些子视图就会保留。
  • 从 iOS 6 开始,视图不再卸载,而是加载一次,然后只要它们的控制器在那里就一直存在。所以强大的属性并不重要。它们也不会创建强引用循环,因为它们指向强引用图。

That said, I am torn between using

也就是说,我在使用之间左右为难

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

and

@property (nonatomic) IBOutlet UIButton *button;

in iOS 6 and after:

在 iOS 6 及之后:

  • Using weakclearly states that the controller doesn't want ownership of the button.

  • But omitting weakdoesn't hurt in iOS 6 without view unloading, and is shorter. Some may point out that is also faster, but I have yet to encounter an app that is too slow because of weakIBOutlets.

  • Not using weakmay be perceived as an error.

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

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

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

Bottom line: Since iOS 6 we can't get this wrong anymore as long as we don't use view unloading. Time to party. ;)

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

回答by Christopher Pickslay

I don't see any problem with that. Pre-ARC, I've always made my IBOutlets assign, as they're already retained by their superviews. If you make them weak, you shouldn't have to nil them out in viewDidUnload, as you point out.

我看不出有什么问题。Pre-ARC,我总是制作我的 IBOutlets assign,因为它们已经被他们的超级视图保留了。如果你制作了它们weak,你不应该像你指出的那样在 viewDidUnload 中将它们清零。

One caveat: You can support iOS 4.x in an ARC project, but if you do, you can't use weak, so you'd have to make them assign, in which case you'd still want to nil the reference in viewDidUnloadto avoid a dangling pointer. Here's an example of a dangling pointer bug I've experienced:

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

A UIViewController has a UITextField for zip code. It uses CLLocationManager to reverse geocode the user's location and set the zip code. Here's the delegate callback:

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

I found that if I dismissed this view at the right time and didn't nil self.zip in viewDidUnload, the delegate callback could throw a bad access exception on self.zip.text.

我发现如果我在正确的时间关闭了这个视图并且没有将 self.zip in 置零viewDidUnload,那么委托回调可能会在 self.zip.text 上抛出一个错误的访问异常。

回答by onmyway133

IBOutletshould be strong, for performance reason. See Storyboard Reference, Strong IBOutlet, Scene Dock in iOS 9

IBOutlet出于性能原因,应该很强。参见iOS 9 中的 Storyboard Reference、Strong IBOutlet、Scene Dock

As explained in this paragraph, the outlets to subviews of the view controller's view can be weak, because these subviews are already owned by the top-level object of the nib file. However, when an Outlet is defined as a weak pointer and the pointer is set, ARC calls the runtime function:

id objc_storeWeak(id *object, id value);

This adds the pointer (object) to a table using the object value as a key. This table is referred to as the weak table. ARC uses this table to store all the weak pointers of your application. Now, when the object value is deallocated, ARC will iterate over the weak table and set the weak reference to nil. Alternatively, ARC can call:

void objc_destroyWeak(id * object)

Then, the object is unregistered and objc_destroyWeak calls again:

objc_storeWeak(id *object, nil)

This book-keeping associated with a weak reference can take 2–3 times longer over the release of a strong reference. So, a weak reference introduces an overhead for the runtime that you can avoid by simply defining outlets as strong.

如本段所述,视图控制器视图的子视图的出口可能很弱,因为这些子视图已经由 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 倍的时间。因此,弱引用引入了运行时开销,您可以通过简单地将出口定义为强引用来避免这种开销。

As of Xcode 7, it suggests strong

从 Xcode 7 开始,它建议 strong

If you watch WWDC 2015 session 407 Implementing UI Designs in Interface Builder, it suggests (transcript from http://asciiwwdc.com/2015/sessions/407)

如果您观看 WWDC 2015 会议 407在 Interface Builder 中实现 UI 设计,它会建议(抄本来自http://asciiwwdc.com/2015/sessions/407

And the last option I want to point out is the storage type, which can either be strong or weak.

In general you should make your outlet strong, especially if you are connecting an outlet to a sub view or to a constraint that's not always going to be retained by the view hierarchy.

The only time you really need to make an outlet weak is if you have a custom view that references something back up the view hierarchy and in general that's not recommended.

So I'm going to choose strong and I will click connect which will generate my outlet.

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

一般来说,你应该让你的插座强大,尤其是当你将插座连接到子视图或约束时,视图层次结构并不总是会保留。

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

所以我将选择strong,然后单击connect,这将生成我的插座。

回答by Giuseppe

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

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

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.

在 Mac 开发中,IBOutlet 通常是一个弱引用:如果您有 NSViewController 的子类,则只会保留顶级视图,并且当您取消分配控制器时,它的所有子视图和插座都会自动释放。

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.

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

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).

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

回答by syedfa

One thing I wish to point out here, and that is, despite what the Apple engineers have stated in their own WWDC 2015 video here:

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

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

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

Apple keeps changing their mind on the subject, which tells us that there is no single right answer to this question. To show that even Apple engineers are split on this subject, take a look at Apple's most recent sample code, and you'll see some people use weak, and some don't.

Apple 一直在改变他们对这个主题的看法,这告诉我们这个问题没有唯一的正确答案。为了表明即使是 Apple 工程师在这个问题上也存在分歧,请查看 Apple 最新的示例代码,您会看到有些人使用了weak,有些人则没有。

This Apple Pay example uses weak: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_swift-DontLinkElementID_8

此 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

As does this picture-in-picture example: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID_4

与此画中画示例一样:https: //developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID_4

As does the Lister example: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Lister 示例也是如此:https: //developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

As does the Core Location example: 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/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElementID_6

As does the view controller previewing example: 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

与视图控制器预览示例一样:https: //developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewPreviewUsingDelegateDelegateDelegateDetailController_PreviewPreviewUsingDelegateDelegate-

As does the HomeKit example: 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

与 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_ActionSetViewControllerID_swift-Dont3

All those are fully updated for iOS 9, and all use weak outlets. From this we learn that A. The issue is not as simple as some people make it out to be. B. Apple has changed their mind repeatedly, and C. You can use whatever makes you happy :)

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

Special thanks to Paul Hudson (author of www.hackingwithsift.com) who gave me the clarification, and references for this answer.

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

I hope this clarifies the subject a bit better!

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

Take care.

小心。

回答by Johannes

From WWDC 2015 there is a session on Implementing UI Designs in Interface Builder. Around the 32min mark he says that you always want to make your @IBOutletstrong.

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

回答by landonandrey

Be aware, IBOutletCollectionshould be @property (strong, nonatomic).

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

回答by Julian Król

It looks like something has changed over the years and now Apple recommends to use strong in general. The evidence on their WWDC session is in session 407 - Implementing UI Designs in Interface Builderand starts at 32:30. My note from what he says is (almost, if not exactly, quoting him):

这些年来似乎发生了一些变化,现在 Apple 建议总体上使用 strong。他们的 WWDC 会议的证据在会议 407 - 在界面生成器中实现 UI 设计,并于 32:30 开始。我对他所说的话的注释是(几乎,如果不完全是,引用他的话):

  • outlet connections in general should be strong especially if we connect a subview or constraint that is not always retained by the view hierarchy

  • weak outlet connection might be needed when creating custom views that has some reference to something back up in the view hierarchy and in general it is not recommended

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

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

In other wards it should be always strong now as long as some of our custom view doesn't create a retain cycle with some of the view up in the view hierarchy

在其他病房里,只要我们的一些自定义视图不会在视图层次结构中创建一个保留循环,那么它现在应该总是很强大

EDIT :

编辑 :

Some may ask the question. Does keeping it with a strong reference doesn't create a retain cycle as the root view controller and the owning view keeps the reference to it? Or why that changed happened? I think the answer is earlier in this talk when they describe how the nibs are created from the xib. There is a separate nib created for a VC and for the view. I think this might be the reason why they change the recommendations. Still it would be nice to get a deeper explanation from Apple.

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