"initialize" class method for classes in Swift?

I'm looking for behavior similar to Objective-C's +(void)initialize class method, in that the method is called once when the class is initialized, and never again thereafter.

A simple class init () {} in a class closure would be really sleek! And obviously when we get to use "class vars" instead of "static vars in a struct closure", this will all match really well!

67006 次浏览

If you have an Objective-C class, it's easiest to just override +initialize. However, make sure subclasses of your class also override +initialize or else your class's +initialize may get called more than once! If you want, you can use dispatch_once() (mentioned below) to safeguard against multiple calls.

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

 

If you have a Swift class, the best you can get is dispatch_once() inside the init() statement.

private var once = dispatch_once_t()


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

This solution differs from +initialize (which is called the first time an Objective-C class is messaged) and thus isn't a true answer to the question. But it works good enough, IMO.

There is no type initializer in Swift.

“Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.”

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.


You could use a type property which default value is a closure. So the code in the closure would be executed when the type property (or class variable) is set.

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

But class stored properties not yet supported (tested in Xcode 8).

One answer is to use static, it is the same as class final.

Good link for that is

Setting a Default Property Value with a Closure or Function

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.


Code example:

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

Prints

start

setting default property value with a closure

So it is lazy evaluated.

I can't find any valid use case to have something like +[initialize] in Swift. Maybe this explains way it does not exist

Why do we need +[initialize] in ObjC?

To initialize some global variable

static NSArray *array;


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

which in Swift

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

To do some hack

+ (void)initialize {
swizzle_methodImplementation()
}

which is not supported by Swift (I can't figure out how to do it for pure Swift class/struct/enum)

For @objc classes, class func initialize() definitely works, since +initialize is implemented by the Objective-C runtime. But for "native" Swift classes, you'll have to see the other answers.

@aleclarson nailed it, but as of recent Swift 4 you cannot directly override initialize. You still can achieve it with Objective-C and categories for classes inheriting from NSObject with a class / static swiftyInitialize method, which gets invoked from Objective-C in MyClass.m, which you include in compile sources alongside 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

If your class cannot inherit from NSObject and using +load instead of +initialize is a suitable fit, you can do something like this:

# 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

There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️

You can use stored type properties instead of initialize method.

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

But since stored types properties are actually lazily initialized on their first access, you will need refer them somewhere. You can do this with ordinary stored property:

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