【问题标题】:Create object of Objective-C class at runtime in Swift, which conforms to Objective-C protocol在 Swift 中运行时创建 Objective-C 类的对象,符合 Objective-C 协议
【发布时间】:2019-01-11 06:44:24
【问题描述】:

我的 Objective-C 协议和接口实现如下:

@protocol Animal <NSObject>
-(void)walk;
@end

@interface Cat : NSObject<Animal>
@end

@implementation Cat
-(void)walk{}
@end

@interface Dog : NSObject<Animal>
@end

@implementation Dog
-(void)walk{}
@end

我正在尝试在运行时使用实现协议“Animal”的类的实例。这段代码在 swift 中:

var classesCount = objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(classesCount))
classesCount = objc_getClassList(AutoreleasingUnsafeMutablePointer(allClasses), classesCount)
for i in 0..<classesCount{
    let cls : AnyClass! = allClasses[Int(i)]
    if class_conformsToProtocol(cls, Animal.self){
        let instance = cls.self.init()
        instance.walk()
    }
}

尝试了多种方法从 AnyClass、AnyObject 和 NSObject 获取实例。我这样做时面临编译器错误。此代码 sn-p 的错误是:

'required'初始化器'init(arrayLiteral:)'必须由'NSSet'的子类提供。

有什么方法可以获取 'Cat' 和 'Dog' 的实例?

【问题讨论】:

    标签: objective-c swift objective-c-runtime


    【解决方案1】:

    让我们在Animal 上定义一个noise 方法进行测试:

    @protocol Animal <NSObject>
    - (NSString *)noise;
    @end
    

    另外,让我们使用一个普通的 Swift 数组来保存类列表:

    let allClassesCount = objc_getClassList(nil, 0)
    var allClasses = [AnyClass](repeating: NSObject.self, count: Int(allClassesCount))
    allClasses.withUnsafeMutableBufferPointer { buffer in
        let autoreleasingPointer = AutoreleasingUnsafeMutablePointer<AnyClass>(buffer.baseAddress)
        objc_getClassList(autoreleasingPointer, allClassesCount)
    }
    

    然后,当我们找到一个符合Animal的类时,让我们将其转换为适当的Swift类型((NSObject &amp; Animal).Type),这样当我们实例化它时,我们就会得到一个适当类型的对象(NSObject &amp; Animal) :

    for aClass in allClasses {
        if class_conformsToProtocol(aClass, Animal.self) {
            let animalClass = aClass as! (NSObject & Animal).Type
    
            // Because animalClass is `(NSObject & Animal).Type`:
            // - It has the `init()` of `NSObject`.
            // - Its instances are `NSObject & Animal`.
            let animal = animalClass.init()
    
            // Because animal is `NSObject & Animal`, it has the `noise` method of `Animal`.
            print(animal.noise())
        }
    }
    

    输出:

    woof
    meow
    

    旁注。你可能认为你可以通过这样做来避免使用class_conformsToProtocol

    if let animalClass = aClass as? (NSObject & Animal).Type {
        let animal = animalClass.init()
        print(animal.noise())
    }
    

    但是你会在运行时崩溃:

    *** CNZombie 3443: -[ conformsToProtocol:] sent to deallocated instance 0x7fffa9d265f0
    

    在幕后,as? 测试使用普通的 Objective-C 消息将conformsToProtocol: 消息发送到aClass。但是系统框架中有各种“僵尸类”,当任何消息发送给它们时会崩溃。这些类用于检测 use-after-free 错误。 class_conformsToProtocol 函数不使用 Objective-C 消息传递,因此它避免了这些崩溃。

    【讨论】:

    • 嘿,罗伯,非常感谢您的回答。它解决了我的问题。也试过你提到的边注的东西。感谢您提供更多信息。更好地理解“as”运算符的工作原理! :)
    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    相关资源
    最近更新 更多