0

I need to create objects from string names that I am reading from a script. I don't want to use the Objective-C runtime.

In my C++ implementation, each class registered itself with an object factory singleton through a global static variable. When the dll loads, the globals were initialized, and all available classes were registered.

I don't want the object factory to have hard coded pre-knowledge of all possible types.

In Swift, all globals are lazily initialized so my C++ registration strategy doesn't work.

Is there some init API that swift calls once per module load? If not, does anyone have a good idea for class registration?

public enum DynamicTypeFactoryError : ErrorType {
    case ClassNotRegistered
}

public protocol DynamicType {
    static var dynamicClassName: String { get }
    init()
}

public struct DynamicTypeRegistraion<T: DynamicType> {
    public init() {
        DynamicTypeFactory.inst.register(T.dynamicClassName, factory: { T() })
    }
}

//===========================================================================
// singleton
public class DynamicTypeFactory {
    // properties
    public static let inst = DynamicTypeFactory()
    typealias ClassFactoryType = (Void) -> DynamicType
    var registry = [String : ClassFactoryType]()

    // methods
    public func create(className: String) throws -> DynamicType {
        // make sure the class exists
        guard let factory = registry[className] else {
            throw DynamicTypeFactoryError.ClassNotRegistered
        }
        return factory()
    }

    /// This is used to register an object so it can be dynamically created
    /// from a string.
    public func register(className: String, factory: (Void) -> DynamicType) {
        if (registry[className]) != nil {
            // TODO - this should be logged
            assertionFailure("Class: \(className) is already registered")
        } else {
            registry[className] = factory
        }
    }
}

//===========================================================================
// MyObject
public struct MyObject : DynamicType {
    // properties
    static let registration = DynamicTypeRegistraion<MyObject>()
    public static var dynamicClassName = "MyObject"
    public init() {
    }
}

// Usage
let myObj = try? DynamicTypeFactory.inst.create("MyObject")

Since MyObject's static registration is not initialized, calling create fails because it hasn't been registered yet.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Ed Connell
  • 53
  • 5
  • There is (as far as I know) no such mechanism in Swift. See for example the discussion in http://stackoverflow.com/questions/28422468/method-load-defines-objective-c-class-method-load-which-is-not-permitted-by or http://stackoverflow.com/questions/24898453/swift-objective-c-load-class-method. – Martin R Apr 15 '16 at 18:08
  • Could you give an example of what you are doing and why lazy initialization doesn't work for your case? Perhaps there's a work-around that will become apparent with an example. –  Apr 15 '16 at 18:20

2 Answers2

0

After reviewing the links posted by Martin R, it appears there is no "non-lazy" initialization of statics, and this is by design. So a different approach will be needed for Swift applications. Thanks Martin!

Ed Connell
  • 53
  • 5
0

There are two methods in Objective C which are used to load and initialize a class. +load and +initialize 1. In swift you can use "public override class func initialize()" to put your initialization code, please note that it will be called lazily.

  1. Support for overriding load was removed in Swift 1.2

Here is what docs say about initialize method

"The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses."

Ashif Khan
  • 9
  • 1
  • 4