Xcode 13.3 警告:“'self' 引用方法'{object}.self',这可能是意外的
Xcode 13.3 warning: "'self' refers to the method '{object}.self', which may be unexpected
我刚刚更新到 Xcode 13.3,我看到了几个新警告实例,这是我在以前的 Xcode 版本中没有看到的。例如,我有一个名为 LabelAndSwitchTableViewCell 的简单 table 视图单元格,如下所示:
import UIKit
class LabelAndSwitchTableViewCell: UITableViewCell {
private let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let _switch: UISwitch = {
let _switch = UISwitch()
_switch.translatesAutoresizingMaskIntoConstraints = false
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
return _switch
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(label)
contentView.addSubview(_switch)
// layout constraints removed for brevity
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func didToggleSwitch() {
print("Switch was toggled...")
}
}
如您所见,我正在向开关添加一个目标,当开关的值发生变化时我希望调用该目标:
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
更新到 Xcode 13.3 后,我现在在这一行看到一个新警告:
'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
Xcode 消除此警告的建议是替换:
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
...与...
_switch.addTarget(LabelAndSwitchTableViewCell.self, action: #selector(didToggleSwitch), for: .valueChanged)
进行此更改会使警告静音,但它也会导致应用程序在我切换开关时崩溃(无法识别的选择器)。这是崩溃的转储:
[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8'
制作 didToggleSwitch()
方法 static
将防止崩溃,但我不确定为什么要这样做。我显然可以恢复更改(从 LabelAndSwitchTableViewCell.self
恢复到 self
),但我想知道我是否应该做其他事情来解决这个问题?
您可以通过将 lets 更改为惰性变量来修复
private lazy var _switch2: UISwitch = {
let _switch = UISwitch()
_switch.translatesAutoresizingMaskIntoConstraints = false
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
return _switch
}()
Xcodefix-it的建议是错误的。
原因是 self
在对象初始化的第 1 阶段尚未准备好。第一阶段是设置所有存储的属性,只有第二阶段才能访问self
.
要修复您的代码,您可以使用 lazy
属性,其中初始化阶段 1 已完成。
参考资料如下:
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
对于此警告:
- 如果没有superclass,或者superclass不是
NSObject
,self
在存储的[=59=中使用时是错误的]直接。
- 如果超级 class 是
NSObject
,self
被编译为自动关闭 (ClassXXX) -> () -> ClassXXX
。所以在运行时,self 将被用作当前实例。但是,由于 Swift 5.6 就此警告我们,我认为在未来的 swift 版本中可能不允许在 NSObject subclass 的存储 属性 中引用 self 。
例1:没有super时报错class
示例2:AST中编译的self
对于此代码:
import Foundation
class MyTest: NSObject {
var myself = self
}
这是编译后的 AST 的一部分:
LabelAndSwitchTableViewCell.self 如建议的那样,在大多数情况下不起作用。使用 nil 并搜索响应链。
我刚刚更新到 Xcode 13.3,我看到了几个新警告实例,这是我在以前的 Xcode 版本中没有看到的。例如,我有一个名为 LabelAndSwitchTableViewCell 的简单 table 视图单元格,如下所示:
import UIKit
class LabelAndSwitchTableViewCell: UITableViewCell {
private let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let _switch: UISwitch = {
let _switch = UISwitch()
_switch.translatesAutoresizingMaskIntoConstraints = false
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
return _switch
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(label)
contentView.addSubview(_switch)
// layout constraints removed for brevity
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func didToggleSwitch() {
print("Switch was toggled...")
}
}
如您所见,我正在向开关添加一个目标,当开关的值发生变化时我希望调用该目标:
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
更新到 Xcode 13.3 后,我现在在这一行看到一个新警告:
'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
Xcode 消除此警告的建议是替换:
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
...与...
_switch.addTarget(LabelAndSwitchTableViewCell.self, action: #selector(didToggleSwitch), for: .valueChanged)
进行此更改会使警告静音,但它也会导致应用程序在我切换开关时崩溃(无法识别的选择器)。这是崩溃的转储:
[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8'
制作 didToggleSwitch()
方法 static
将防止崩溃,但我不确定为什么要这样做。我显然可以恢复更改(从 LabelAndSwitchTableViewCell.self
恢复到 self
),但我想知道我是否应该做其他事情来解决这个问题?
您可以通过将 lets 更改为惰性变量来修复
private lazy var _switch2: UISwitch = {
let _switch = UISwitch()
_switch.translatesAutoresizingMaskIntoConstraints = false
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
return _switch
}()
Xcodefix-it的建议是错误的。
原因是 self
在对象初始化的第 1 阶段尚未准备好。第一阶段是设置所有存储的属性,只有第二阶段才能访问self
.
要修复您的代码,您可以使用 lazy
属性,其中初始化阶段 1 已完成。
参考资料如下:
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
对于此警告:
- 如果没有superclass,或者superclass不是
NSObject
,self
在存储的[=59=中使用时是错误的]直接。 - 如果超级 class 是
NSObject
,self
被编译为自动关闭(ClassXXX) -> () -> ClassXXX
。所以在运行时,self 将被用作当前实例。但是,由于 Swift 5.6 就此警告我们,我认为在未来的 swift 版本中可能不允许在 NSObject subclass 的存储 属性 中引用 self 。
例1:没有super时报错class
示例2:AST中编译的self
对于此代码:
import Foundation
class MyTest: NSObject {
var myself = self
}
这是编译后的 AST 的一部分:
LabelAndSwitchTableViewCell.self 如建议的那样,在大多数情况下不起作用。使用 nil 并搜索响应链。