为什么Xcode不使用无主引用而是弱引用来创建outlets?

3
Xcode将outlets生成为带有隐式解包的弱变量,例如:
@IBOutlet weak var nameTextField: UITextField!

我想知道为什么它不直接使用onowned var,在我理解中,它的行为与var完全相同,但是类型保持为非可选类型。这两者之间有什么区别吗?

weak var foo: UITextField!
unowned var foo: UITextField

1
2阶段初始化。插座在初始化后分配,因此它们必须是可选的。通常它们是weak或者strong并不重要,但是unowned是不正确的。 - Sulthan
@Sulthan 我一直想知道为什么 IBOutlet 在断点上会被触发两次。所以我创建了一个项目,将标签拖到 viewController 中并在其上设置了断点。第一次断点发生在 viewDidLoadinit 之前。如果我打印标签,它显示为 nil。第二次触发断点是在 init 之后但仍在 viewDidLoad 之前。这次如果我打印它,它会显示:Optional<UILabel> - mfaani
5个回答

6
一个weak变量有一个默认值,即nil,因此您的代码是合法的,因为在对象创建时(连接点之前),outlet属性已经有一个值。
但是,如果使用unowned变量,它将没有默认值,您的代码将无法编译。可以试一下。
此外,整个概念都是错误的。 unowned用于具有保证独立存在且无法缺少的事物。视图控制器视图的子视图都不符合这些条件。

我认为这不是原因。声明outlet为weak的原因是outlet所在的容器视图始终会强引用它。它不能被声明为unowned,因为Objc运行时不支持unowned。而weak没有默认值nil。Outlet被隐式解包。因此,在访问时应该始终具有非nil值。 - Mohammad Sadiq
@MohammadSadiq 因为 Objc 运行时不支持 unowned。这是 Objective-C 中的非 ARC weak - matt

0

是的,有区别。除了默认值问题外,还有一种方法可以检查weak值是否存在:

if let nameTextField = nameTextField {
    // do smth
}

另一方面,我认为没有办法检查unowned是否存在并且可以访问。每当使用unowned时,它应该始终存在,但在IBOutlet的情况下并非如此。直到从故事板加载视图控制器之前,outlets才会被设置。

希望这有所帮助!


0

未拥有类型是危险的,最好避免使用。未拥有变量相当于 Objective C 的 unsafe_unretained 类型。

如果未拥有引用所指向的对象被释放,未拥有引用将不会被设置为 nil。如果您稍后尝试引用该对象,则您的代码无法确定它是否仍然有效。如果您尝试调用方法或读/写实例变量,则可能会崩溃,如果对象已被释放。

(还有一个事实,即变量没有默认值,正如马特在他的答案中所说。)


隐式解包可选项也会导致程序崩溃。我的理解是,Outlets 是 unowned 的好选择,但由于 unowned 在 Obc 运行时不可用,因此被声明为 weak,同时又是隐式解包的。 - Mohammad Sadiq
虽然隐式解包可选项在为nil时会崩溃,但这种崩溃是保证的、立即的和明显的。对已释放对象的未拥有引用会导致未定义的行为,并可能导致内存损坏,这些问题可能直到以后才会出现。这些错误非常棘手且难以发现。 - Duncan C
不,Outlets 不适合使用 unowned。你应该尽可能避免使用 unowned 引用,并在可以的情况下使用 weak。 - Duncan C
好的。但这真的是电源插座弱和隐式解包的原因吗?因为隐式解包本身意味着在访问时不应该为 nil。 - Mohammad Sadiq
1
实际上,unowned 引用在尝试访问已释放的对象时总是会引发运行时错误(因此在这方面等同于强制解包 weak 引用)。unowned(unsafe) 才会给您带来未定义的行为。运行时知道要引发运行时错误的方式实际上是因为 Swift 为无主引用保留了单独的引用计数。当强引用计数为零时,将调用 deinit,然后从 unowned 访问中引发运行时错误。然后当 unowned 引用计数达到零时,对象才会 实际 被释放。 - Hamish
尽管像许多其他安全检查一样(例如强制解包时的运行时错误),非法未拥有访问引发的运行时错误将在-Ounchecked构建中被删除,此时您将获得未定义的行为。但通常情况下,您应避免使用-Ounchecked构建。有关更多信息,请参见https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID381和https://github.com/apple/swift/blob/master/stdlib/public/SwiftShims/RefCount.h#L54中的头文件注释。 - Hamish

0

unowned var foo: UITextField 应该在视图控制器初始化期间进行初始化,但这是不可能的,因为outlet只能在视图创建后进行初始化,而视图只有在视图控制器显示时(更准确地说是当访问view属性时)才会被创建。


是的,我认为这就是它使用weak的原因。在初始化方面,unowned的行为与任何其他属性相同。因此,如果要使用unowned,就需要使用可选项,但是当我们有weak时,这并没有太多意义 :) - Robo Robok

0

以前可选项不能是unowned。现在可以了,所以unowned是合适的。这可能不会自动完成,因为它会让某些人感到困惑。

@IBOutlet private unowned var uiObject: UIObject!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接