【问题标题】:"initialize" class method for classes in Swift?Swift中类的“初始化”类方法?
【发布时间】:2014-06-10 09:16:42
【问题描述】:

我正在寻找类似于 Objective-C 的 +(void)initialize 类方法的行为,因为该方法在类被初始化时被调用一次,之后再也不会被调用。

class 封口中的简单class init () {} 将非常时尚!显然,当我们在结构闭包中使用“class vars”而不是“static vars”时,这一切都将非常匹配!

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    如果您有一个 Objective-C 类,最简单的方法就是覆盖 +initialize。但是,请确保您的类的 子类 也覆盖 +initialize,否则您的类的 +initialize 可能会被多次调用!如果需要,您可以使用dispatch_once()(如下所述)来防止多次调用。

    class MyView : UIView {
      override class func initialize () {
        // Do stuff
      }
    }
    

     

    如果你有一个 Swift 类,你能得到的最好的就是在 init() 语句中的 dispatch_once()

    private var once = dispatch_once_t()
    
    class MyObject {
      init () {
        dispatch_once(&once) {
          // Do stuff
        }
      }
    }
    

    此解决方案不同于 +initialize(在第一次向 Objective-C 类发送消息时调用),因此不是该问题的真正答案。但它足够好用,IMO。

    【讨论】:

    • 你应该使用dispatch_once
    • @BryanChen 为什么?在 Objective C 中,initialize 保证只被调用一次。有变化吗?
    • 这与 Objective-C 中的 +initialize 不同。 Objective-C 中的+initializeclass 第一次messaged 时被调用。这在第一次创建类的实例时被调用。
    • 从 Swift 3.1 (Xcode 8.3) 开始,使用 override class func initialize() 现在会生成一个警告,包括“......不保证被 Swift 调用,并且在未来的版本中将被禁止”。见:stackoverflow.com/questions/42824541/…
    • 从 Swift 4 开始,使用 override class func initialize() 是一个错误。
    【解决方案2】:

    Swift 中没有类型初始化器

    “与存储实例属性不同,您必须始终为存储类型属性赋予默认值。这是因为类型本身没有初始化器,它可以在初始化时为存储的类型属性赋值。”

    摘自:Apple Inc. “Swift 编程语言”。 iBooks.


    你可以使用一个 type 属性,它的默认值是一个闭包。所以闭包中的代码会在 type 属性(或类变量)被设置时执行。

    class FirstClass {
        class var someProperty = {
         // you can init the class member with anything you like or perform any code
            return SomeType
        }()
    }
    

    但是class stored properties not yet supported(在 Xcode 8 中测试)。

    一个答案是使用static,它与class final相同。

    很好的链接是

    使用闭包或函数设置默认属性值

    摘自:Apple Inc. “Swift 编程语言”。 iBooks.


    代码示例:

    class FirstClass {
        static let someProperty = {
            () -> [Bool] in
            var temporaryBoard = [Bool]()
            var isBlack = false
            for i in 1...8 {
                for j in 1...8 {
                    temporaryBoard.append(isBlack)
                    isBlack = !isBlack
                }
                isBlack = !isBlack
            }
    
            print("setting default property value with a closure")
            return temporaryBoard
        }()
    }
    
    print("start")
    FirstClass.someProperty
    

    打印

    开始

    使用闭包设置默认属性值

    所以它是惰性求值的。

    【讨论】:

    • 您现在可以使用static 属性来执行此操作... static var someProperty = { return SomeType }() ... 但问题是该代码是延迟运行的,第一次引用该属性时。 developer.apple.com/swift/blog/?id=7
    • @iGodric “所以闭包中的代码将在设置类型属性(或类变量)时执行”。你能澄清一下吗?究竟什么时候执行闭包?
    • @JarrodSmith 我扩展了答案以显示一个示例。
    • 为什么我们需要一个闭包?它可以在没有关闭的情况下工作吗?还是因为它是一个静态变量?
    • @VanDuTran 如果你想在创建类时使用类初始化器,那就是应用程序加载方法。但是 +initialize 的有趣行为是,它是惰性的(仅在第一次使用类时加载值)。对于懒惰的我使用了闭包。
    【解决方案3】:

    对于@objc 类,class func initialize() 绝对有效,因为+initialize 是由 Objective-C 运行时实现的。但对于“原生”Swift 类,您必须查看其他答案。

    【讨论】:

    • 我有一个继承自 NSObject 的 swift 类。我注意到我的类的+initialize(实际上是override class func initialize())在我调用类的方法之前不会被调用。访问属性似乎不会触发它。也许这是因为 swift 属性不是底层的方法(getter 和 setter),就像在 Objective-C 中一样?
    • @NicolasMiari:你说的是实例方法还是类方法?实例或类属性?在Objective-C中,需要调用类方法才能创建实例,所以我认为创建实例的行为应该在您使用实例方法或实例属性之前触发它。如果你在谈论类属性,Objective-C 没有类属性,因此 Swift 的类属性可能与 ObjC 运行时不兼容,因为不需要兼容(只是猜测,我没有检查源)。
    • @NicolasMiari:+[NSObject initialize] 的文档确实说它在类“发送第一条消息”之前被调用,所以没有理由认为类属性会触发它。
    • 类属性,我明白了......这些在 Objective-C 中确实没有对应物,但是您可以通过实现两个签名兼容的类方法来伪造它们(合成) 实例属性的 getter 和 setter。例如,+(void)setColor:(UIColor*) newColor;和 +(UIColor*) 颜色;我猜 swift 类属性是完全不同的野兽。
    【解决方案4】:

    您可以使用存储类型属性代替initialize 方法。

    class SomeClass: {
      private static let initializer: Void = {
        //some initialization
      }()
    }
    

    但由于存储类型属性实际上是在第一次访问时延迟初始化的,因此您需要在某个地方引用它们。您可以使用普通的存储属性来做到这一点:

    class SomeClass: {
      private static let initializer: Void = {
        //some initialization
      }()
      private let initializer: Void = SomeClass.initializer
    }
    

    【讨论】:

    • 会很好,但它很快吗?据我所知,在 swift 'let' 声明中不能计算属性。
    • @Aquajet: initializer 不是计算属性,因为分配的闭包实际上是执行的。注意末尾的等号和括号。正如答案中提到的,静态属性是延迟评估的,因此在加载类时不会立即执行闭包。相反,它是通过在代码中的其他地方引用属性来触发的。
    【解决方案5】:

    @aleclarson 做到了,但从最近的 Swift 4 开始,你不能直接覆盖 initialize。您仍然可以使用 Objective-C 和继承自 NSObject 的类的类别来实现它,使用类/静态 swiftyInitialize 方法,该方法从 MyClass.m 中的 Objective-C 调用,您将其与 MyClass.swift 一起包含在编译源中:

    # MyView.swift
    
    import Foundation
    public class MyView: UIView
    {
        @objc public static func swiftyInitialize() {
            Swift.print("Rock 'n' roll!")
        }
    }
    
    # MyView.m
    
    #import "MyProject-Swift.h"
    @implementation MyView (private)
        + (void)initialize { [self swiftyInitialize]; }
    @end
    

    如果您的类不能从 NSObject 继承并且使用 +load 而不是 +initialize 是合适的选择,您可以执行以下操作:

    # MyClass.swift
    
    import Foundation
    public class MyClass
    {
        public static func load() {
            Swift.print("Rock 'n' roll!")
        }
    }
    public class MyClassObjC: NSObject
    {
        @objc public static func swiftyLoad() {
            MyClass.load()
        }
    }
    
    # MyClass.m
    
    #import "MyProject-Swift.h"
    @implementation MyClassObjC (private)
        + (void)load { [self swiftyLoad]; }
    @end
    

    有几个问题,尤其是在静态库中使用这种方法时,请查看 Medium 上的完整 post 了解详细信息! ✌️

    【讨论】:

      【解决方案6】:

      我在 Swift 中找不到任何有效的用例,例如 +[initialize]。也许这解释了它不存在的方式

      为什么我们在 ObjC 中需要 +[initialize]

      初始化一些全局变量

      static NSArray *array;
      
      + (void)initialize {
          array = @[1,2,3];
      }
      

      Swift 中的哪个

      struct Foo {
          static let array = [1,2,3]
      }
      

      做一些hack

      + (void)initialize {
          swizzle_methodImplementation()
      }
      

      Swift 不支持(我不知道如何为纯 Swift 类/结构/枚举做到这一点)

      【讨论】:

      • 一个有效的用例是注册 NSUserDefaults
      • 另一个有效的情况是暴露绑定
      • 另一个有效的用例是将类(self)注册为组的一部分。例如CoreImage编程指南建议在+initializeCIFilter的子类中使用+[[CIFilter registerFilterName:constructor:classAttributes:]
      • 另一个有效的用例是为使用随机数的类播种一次随机函数。
      猜你喜欢
      • 2014-12-29
      • 1970-01-01
      • 2021-01-17
      • 1970-01-01
      • 2019-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-15
      相关资源
      最近更新 更多