“initialize” class method for classes in Swift?

后端 未结 6 1065
日久生厌
日久生厌 2020-12-04 11:55

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 a

相关标签:
6条回答
  • 2020-12-04 12:18

    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.

    0 讨论(0)
  • 2020-12-04 12:21

    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.

    0 讨论(0)
  • 2020-12-04 12:28

    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.

    0 讨论(0)
  • 2020-12-04 12:28

    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)

    0 讨论(0)
  • 2020-12-04 12:33

    @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! ✌️

    0 讨论(0)
  • 2020-12-04 12:43

    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
    }
    
    0 讨论(0)
提交回复
热议问题