Xcode 13.3 警告:“'self' 指的是方法 '{object}.self',这可能是意外的

Xcode 13.3 warning: "'self' refers to the method '{object}.self', which may be unexpected

提问人:bmt22033 提问时间:3/21/2022 更新时间:4/7/2022 访问量:5468

问:

我刚刚更新到 Xcode 13.3,我看到了几个新警告的实例,这是我在以前版本的 Xcode 中没有看到的。例如,我有一个名为 LabelAndSwitchTableViewCell 的简单表视图单元格,如下所示:

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()staticLabelAndSwitchTableViewCell.selfself

iOS Swift Xcode 警告 选择器

评论


答:

46赞 Shadowrun 3/22/2022 #1

您可以通过将 lets 更改为 lazy var 来修复

private lazy var _switch2: UISwitch = {
    let _switch = UISwitch()
    _switch.translatesAutoresizingMaskIntoConstraints = false
    _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
    return _switch
}()

Xcode 修复建议是错误的。

评论

0赞 bmt22033 3/22/2022
没有尝试过让我的道具成为懒惰的 vars,但你是绝对正确的 - 这确实阻止了警告!不确定我是否完全理解为什么在这种特殊情况下延迟启动 switch 属性会有所不同,但我当然想不出缺点,所以我会相应地更新我的代码。谢谢你的建议!
3赞 Shadowrun 3/22/2022
我不知道这是 13.3 中的一个错误,还是存在一些真正的问题,即不能保证在自动评估闭包中自我存在、启动和捕获
1赞 Gábor Hertelendy 3/26/2022
我认为在某些情况下,这可能是编码错误,但直到现在才发出警告。(目前警告和更正令人困惑)当目标添加到此闭包中时,self 绝对不可用,但在大多数情况下也不需要准备好。例如,选择器事件可能是 .allEvents,并且某些事件发生在自就绪之前 + 您需要处理该事件。我相信这是一个非常不可能的情况,你需要一个事件在自我之前进行控制。懒惰只是让它让这个 var 在 self 之后进入。
24赞 Simon Wang 3/22/2022 #2

原因是在对象初始化的第 1 阶段尚未准备就绪。第 1 阶段是设置所有存储的属性,只有在第 2 阶段,您才能访问 .selfself

若要修复代码,可以使用属性,其中初始化阶段 1 已完成。lazy

以下是参考资料:https://docs.swift.org/swift-book/LanguageGuide/Initialization.html enter image description here

对于此警告:

  1. 如果没有超类,或者没有超类,则在直接在存储属性中使用时会出现错误。NSObjectself
  2. 如果超类是 ,则编译为自动闭包。因此,在运行时,self 将用作当前实例。但是,由于 Swift 5.6 警告我们这一点,我认为在未来的 swift 版本中可能不允许在 NSObject 子类的存储属性中引用 self。NSObjectself(ClassXXX) -> () -> ClassXXX

示例 1:没有超类时出错 enter image description here

示例 2:AST 中编译的 self

对于此代码:

import Foundation
class MyTest: NSObject  {
    var myself = self
}

以下是编译的 AST 的一部分:enter image description here

评论

0赞 Shadowrun 3/22/2022
那么,为什么目标操作在运行时起作用,并且正确地以实例自身而不是 ClassXXX.self 为目标
2赞 Simon Wang 3/22/2022
现在,如果一个类是 的子类,swift 编译器将编译成自动闭包:(我已经检查了编译的 AST 和 SIL 语言,这可以确认 的自动闭合)。所以在运行时,将用作当前实例。但是,由于 Swift 5.6 警告我们这一点,我认为在未来的 swift 版本中可能不允许引用子类的 in stored 属性。NSObjectself(ClassXXX) -> () -> ClassXXXselfselfselfNSObject
0赞 Simon Wang 3/22/2022
对答案进行编辑以包含 AST 示例
1赞 bmt22033 3/22/2022
感谢您的额外解释!
0赞 Halfrek 4/7/2022 #3

LabelAndSwitchTableViewCell.self 在大多数情况下不起作用。使用 nil 并搜索响应程序链。

addTarget(_:action:for:)

评论

3赞 Community 4/7/2022
正如目前所写的那样,你的答案尚不清楚。请编辑以添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何写出好答案的更多信息。