Swift中类的“初始化”类方法?


74

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

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

Answers:


61

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

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

 

如果您有Swift类,那么最好的方法就是dispatch_once()init()语句内。

private var once = dispatch_once_t()

class MyObject {
  init () {
    dispatch_once(&once) {
      // Do stuff
    }
  }
}

此解决方案与+initialize(第一次给Objective-C类发送消息时称为)不同,因此不是对这个问题的真正答案。IMO,但是效果很好。


1
您应该使用dispatch_once
Bryan Chen

2
@BryanChen为什么?在目标C中,initialize保证只能被调用一次。改变了吗?
sapi 2014年

2
这与+initializeObjective-C不同。+initialize在Objective-C中首次调用该类消息。这被称为第一次创建类的实例。
newacct

7
从Swift 3.1(Xcode 8.3)开始,使用override class func initialize()now会生成一条警告,包括“ ...不能保证由Swift调用,并且在以后的版本中将不允许使用...。” 请参阅:stackoverflow.com/questions/42824541/...
斯科特Corscadden

5
从Swift 4开始使用override class func initialize()是一个错误。
ThomasW '18

52

Swift中没有类型初始值设定项

与存储实例属性不同,您必须始终为存储类型属性赋予默认值。这是因为类型本身没有可在初始化时将值分配给存储的type属性的初始化程序。”

摘录自:苹果公司“ The Swift Programming Language”。iBooks


您可以使用类型属性,其默认值为闭包。因此,当设置了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

好的链接是

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

摘录自:苹果公司“ The Swift Programming Language”。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
Aneel '16

@iGodric“因此,在设置type属性(或类变量)时,将执行闭包中的代码”。你能澄清一下吗?何时确切执行关闭?
加罗德·史密斯

1
@JarrodSmith我扩展了答案以显示示例。
Binarian

为什么我们需要关闭?没有关闭就可以了吗?还是因为它是一个静态变量?
Van Du Tran

@VanDuTran如果要在创建类时使用类初始化程序,那将只是应用程序加载方法。但是+ initialize的有趣行为是它是惰性的(仅在首次使用class时才加载值)。对于懒惰,我使用了闭包。
Binarian

7

对于@objc类来说,class func initialize()肯定是可行的,因为它+initialize是由Objective-C运行时实现的。但是对于“本机” Swift类,您必须查看其他答案。


我有一个继承自的快速类NSObject。我注意到,直到我调用该类的方法时,才调用我的类+initialize(实际上是override class func initialize())。访问属性似乎不会触发它。也许这是因为swift属性不是像Objective-C中那样是幕后的方法(getter和setter)?
Nicolas Miari

1
@NicolasMiari:您在谈论实例或类方法吗?实例或类的属性?在Objective-C中,需要调用一个类方法才能创建一个实例,因此我认为创建实例的动作应该在甚至使用实例方法或实例属性之前触发它。如果您在谈论类属性,那么Objective-C没有类属性,因此Swift的类属性可能与ObjC运行时不兼容,因为不需要兼容(只是猜测,我没有检查源代码)。
newacct

1
@NicolasMiari:的文档+[NSObject initialize]确实说在类“发送其第一条消息”之前被调用,因此没有理由认为类属性会触发它。
newacct

我看到了类属性……在Objective-C中确实没有对应的类属性,但是您可以通过实现两个类方法伪造它们,这些方法的签名与实例属性的(综合)getter和setter兼容。例如,+(void)setColor:(UIColor *)newColor; 和+(UIColor *)颜色;我想敏捷的类属性是完全不同的野兽。
Nicolas Miari

2

@aleclarson钉了它,但是从最近的Swift 4开始,您不能直接覆盖initialize。您仍然可以使用Objective-C和NSObject使用类/静态swiftyInitialize方法继承的类的类别来实现它,该类从Objective-C中被调用MyClass.m,您可以在中将其包括在编译源中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上的完整文章以获取详细信息!✌️


2

您可以使用存储的类型属性代替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
}

会很好,但是很快吗?据我所知,迅速的“ let”声明无法计算属性。
Aquajet

3
@Aquajet:initializer不是已计算的属性,因为分配的闭包实际上已执行。注意等号和结尾处的括号。如答案中所述,静态属性是延迟计算的,因此在加载类时不会立即执行闭包。而是通过在代码的其他地方引用该属性来触发它。
卢卡斯·库巴内克

-5

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

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

初始化一些全局变量

static NSArray *array;

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

在Swift中

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

做一些黑客

+ (void)initialize {
    swizzle_methodImplementation()
}

Swift不支持它(我不知道如何为纯Swift类/结构/枚举执行此操作)


18
一个有效的用例是注册NSUserDefaults
Rick

5
另一个有效的情况是ExposureBinding
AJ Venturella'1

10
另一个有效的用例是将类(自身)注册为组的一部分。例如CoreImage编程指南建议使用+[[CIFilter registerFilterName:constructor:classAttributes:]in+initialize的子类CIFilter
nacho4d

4
另一个有效的用例是为使用随机数的类播种一次随机函数。
克里斯·哈顿
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.