为什么关键字“weak”只能应用于类和绑定类协议类型?

87
当我在Swift中将变量声明为weak时,有时会从Xcode收到以下错误消息:

'weak' may only be applied to class and class-bound protocol types

或者

'weak' must not be applied to non-class-bound 'SomeProtocol'; consider adding a protocol conformance that has a class bound

我想知道为什么关键字weak只能应用于类和类绑定协议类型?这个要求背后的原因是什么?

9
“weak”仅适用于引用计数,并且只有类被引用计数。 - dan
11个回答

137

出现此错误的常见原因是您已经声明了自己的协议,但忘记从 AnyObject 继承:

protocol PenguinDelegate: AnyObject {
    func userDidTapThePenguin()
}

class MyViewController: UIViewController {
    weak var delegate: PenguinDelegate?
}

如果您忘记从 AnyObject 继承,上面的代码将会给你一个错误。原因是 weak 只适用于引用类型(类)。所以,通过明确说明 PenguinDelegate 是用于类而不是值类型,您可以让编译器更加放心。


1
从NSObjectProtocol继承的优点是什么?我的自定义代理没有这样做,但在我的使用中也没有遇到任何问题。 - Apostolos Apostolidis
3
弱引用只在类中有效。通过继承NSObjectProtocol,您向编译器保证该协议仅用于类(而不是枚举等其他对象)。 - Vince O'Sullivan
@VinceO'Sullivan 保证编译器的好处是什么?不继承NSObjectProtocol难道不更容易吗?首先,您不必处理添加weak修饰符的问题。 - yesthisisjoe
12
可以的,它有效。但你不知道为什么。weak 是用于引用类型的限定符,对于值类型而言没有意义,因为根据定义,值类型不能是弱引用类型。协议可以被既是引用类型又是值类型的对象所采用。因此,你应该将它限制为仅由引用类型实现:protocol PenguinDelegate: class { }。在这里,你将协议限制为仅由 NSObjectProtocol 实现,它也是引用类型,这就是它能够工作的原因。 - Martin
4
请使用 protocol PenguinDelegate: class,它不依赖于 Objective-C 运行时,但仍然可以解决这个问题。 - Feuermurmel

84

weak 是用于引用类型的限定符(与值类型相对,如 struct 和内置值类型)。

引用类型允许您对同一对象拥有多个引用。当最后一个强引用停止引用它时,对象将被释放(弱引用不计算在内)。

而值类型则是通过复制分配的。不适用引用计数,因此 weak 修饰符在其上没有意义。


65
protocol PenguinDelegate: class {
    func userDidTapThePenguin()
}

class MyViewController: UIViewController {
    weak var delegate: PenguinDelegate?
}
如果您在协议后面输入class,它也可以正常工作,并且似乎比NSObjectProtocol更合适。

我觉得这个和 @dasblinkenlight 的回答结合起来,能够完整地回答这个问题。@dasblinkenlight 解释了为什么会出现错误信息,而这个回答则解释了作为开发人员你可能想要做什么,该如何实现。 - stuckj

13

万一还有其他人像我一样认为代码完全正确,请检查是否错误地将:替换为=

这是我遇到的问题。它也给我带来了与上述相同的错误:

protocol PenguinDelegate: class {
    func userDidTapThePenguin()
}

class MyViewController: UIViewController {
    weak var delegate = PenguinDelegate?
}

但是正确的方法是:

protocol PenguinDelegate: class {
    func userDidTapThePenguin()
}

class MyViewController: UIViewController {
    weak var delegate: PenguinDelegate?
}

你看出区别了吗?我花了一段时间才发现我用了等号而不是冒号。还要注意,同一行代码中我得到了另一个错误,因为我决定我的第一个错误似乎是真正的问题所在:

- weak 只能应用于类和类相关的协议类型

:- <


5

我发现有一种情况,即使你已经定义了类类型,仍然会收到此错误消息。

例如:

class MyVC: UIViewController {
   var myText: UITextView = {
      [weak self]
      let text = UITextView()
      // some codes using self
      return text
   }()
}

这里通过一个匿名块返回了一个 UITextView 对象作为 var myText 的初始化。我遇到了同样类型的错误消息。要解决这个问题,必须将 var 标记为 lazy

class MyVC: UIViewController {
   lasy var myText: UITextView = {
      [weak self]
      let text = UITextView()
      // some codes using self
      return text
   }()
}

3

2

weak是针对自动引用计数(ARC)的。它表示不增加引用计数。因此,它仅适用于Class。在Swift中,为了安全起见,您将获得可选值。


1

enter image description here我在Swift中使用Objective-C类来创建一个ScrollView。我创建了这个ScrollView的IBOutlet,但是在编译代码时出现了错误。

为了解决这种问题,请在桥接头文件中导入该类。

import "YourClass.h"

我使用的是Xcode 9.2和Swift 3.2。


1

我试图捕获闭包中的字符串和数组类型的属性。我遇到了以下错误:

'weak' 只能应用于类和类绑定的协议类型,而不是 '[String]'

'weak' 只能应用于类和类绑定的协议类型,而不是 'String'

我在 playground 中尝试了一下,结果发现仅捕获 self 就足够处理这些类型了。


0

weak 只适用于引用类型,因此如果您从 struct(而不是 class)调用,则 Xcode 会报告错误。


如果闭包是引用类型,那么为什么XCode会显示错误'weak' may only be applied to class and class-bound protocol types, not '() -> Void' - Kuvonchbek Yakubov

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