当实例属性由静态成员初始化时,为什么不可用

  • 本文关键字:属性 实例 静态成员 初始化 swift
  • 更新时间 :
  • 英文 :


运行以下代码时,当人们期望它为 FooViewController时,defaultModuleName内部的selfReactViewController。为什么?

class ReactViewController: UIViewController {
    var moduleName: String = defaultModuleName
    static var defaultModuleName: String {
        let t = String(reflecting: self) // Also tried NSStringFromClass
        guard let s = t.split(separator: ".").last else { return "" }
        guard let r = s.range(of: "ViewController") else { return "" }
        return String(s.prefix(upTo: r.lowerBound))
    }
}
class FooViewController: ReactViewController {
   override func viewDidLoad() {
       super.viewDidLoad();
       print(moduleName); // Prints "React"
   }
}

这很有趣;看来属性初始评估器中可用的self仅仅是属性所定义的类型,而不是构造实例的动态类型。

一个最小的例子是:

class C {
  static var foo: String { return "(self)" }
  let bar = foo // the implicit 'self' in the call to 'foo' is always C.
}
class D : C {}
print(D().bar) // C

bar的属性启动器中,隐式 selfC.self,而不是 D.self;尽管我们正在构建D实例。这就是对foo的呼吁将其视为self

这还可以防止class成员从属性初始评估器中撤回:

class C {
  class var foo: String { return "C" }
  let bar = foo 
}
class D : C {
  override class var foo: String { return "D" }
}
print(D().bar) // C

因此,我将其视为错误,并在此处提交了报告。

在修复之前,一个简单的解决方案是使用懒惰属性,因为现在self是实际实例(在第一次访问属性时),我们得到的可以使动态获得动态使用type(of: self)的类型。

例如:

class C {
  static var foo: String { return "(self)" }
  // private(set) as the property was a 'let' in the previous example.
  lazy private(set) var bar = type(of: self).foo
}
class D : C {}
print(D().bar) // D

应用于您的示例:

class ReactViewController : UIViewController {
  lazy var moduleName = type(of: self).defaultModuleName
  static var defaultModuleName: String {
    let t = String(reflecting: self) // Also tried NSStringFromClass
    guard let s = t.split(separator: ".").last else { return "" }
    guard let r = s.range(of: "ViewController") else { return "" }
    return String(s.prefix(upTo: r.lowerBound))
  }
}
class FooViewController : ReactViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    print(moduleName) // Prints "Foo"
  }
}

您只需要通过self而不是type(of: self),然后使用String(describing:) Initializer。

class ClassA {
    static var className: String {
        return String(describing: self)
    }
}
class ClassB: ClassA { }
print(ClassB.className) // prints "ClassB"

编辑:澄清var moduleName: String = defaultModuleName更新。假设我将此行添加到上面的示例(同一想法):

class ClassA {
    // This is a property of ClassA -> it gets implicitly initialized
    // when ClassA does -> it uses ClassA.className for its value
    var instanceClassName = className
    static var className: String {
        return String(describing: self)
    }
}
class ClassB: ClassA { }
print(ClassB().instanceClassName) // prints "ClassA"

这个新的instanceClassName不是静态的,因此它是ClassA上的实例属性。因此,当初始化ClassA初始化时(不是)初始化它。Ergo是在ClassA中设置的属性,使用className的引用将打印出ClassA

最新更新