【发布时间】:2014-06-10 09:16:42
【问题描述】:
我正在寻找类似于 Objective-C 的 +(void)initialize 类方法的行为,因为该方法在类被初始化时被调用一次,之后再也不会被调用。
class 封口中的简单class init () {} 将非常时尚!显然,当我们在结构闭包中使用“class vars”而不是“static vars”时,这一切都将非常匹配!
【问题讨论】:
我正在寻找类似于 Objective-C 的 +(void)initialize 类方法的行为,因为该方法在类被初始化时被调用一次,之后再也不会被调用。
class 封口中的简单class init () {} 将非常时尚!显然,当我们在结构闭包中使用“class vars”而不是“static vars”时,这一切都将非常匹配!
【问题讨论】:
如果您有一个 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
initialize 保证只被调用一次。有变化吗?
+initialize 不同。 Objective-C 中的+initialize 在class 第一次messaged 时被调用。这在第一次创建类的实例时被调用。
override class func initialize() 现在会生成一个警告,包括“......不保证被 Swift 调用,并且在未来的版本中将被禁止”。见:stackoverflow.com/questions/42824541/…
override class func initialize() 是一个错误。
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
对于@objc 类,class func initialize() 绝对有效,因为+initialize 是由 Objective-C 运行时实现的。但对于“原生”Swift 类,您必须查看其他答案。
【讨论】:
NSObject 的 swift 类。我注意到我的类的+initialize(实际上是override class func initialize())在我调用类的方法之前不会被调用。访问属性似乎不会触发它。也许这是因为 swift 属性不是底层的方法(getter 和 setter),就像在 Objective-C 中一样?
+[NSObject initialize] 的文档确实说它在类“发送第一条消息”之前被调用,所以没有理由认为类属性会触发它。
您可以使用存储类型属性代替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
}
【讨论】:
initializer 不是计算属性,因为分配的闭包实际上是执行的。注意末尾的等号和括号。正如答案中提到的,静态属性是延迟评估的,因此在加载类时不会立即执行闭包。相反,它是通过在代码中的其他地方引用属性来触发的。
@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 了解详细信息! ✌️
【讨论】:
我在 Swift 中找不到任何有效的用例,例如 +[initialize]。也许这解释了它不存在的方式
+[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 类/结构/枚举做到这一点)
【讨论】:
+initialize的CIFilter的子类中使用+[[CIFilter registerFilterName:constructor:classAttributes:]。