简体   繁体   English

在Swift中的运行时创建符合Objective-C协议的Objective-C类的对象

[英]Create object of Objective-C class at runtime in Swift, which conforms to Objective-C protocol

I have Objective-C Protocol and Interface implementation as below: 我有如下的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

I am trying to use instance of classes at runtime which implement protocol 'Animal'. 我正在尝试在运行时使用实现协议“动物”的类的实例。 This code in in 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()
    }
}

Tried many ways to get instance from AnyClass, AnyObject, and NSObject. 尝试了多种方法来从AnyClass,AnyObject和NSObject获取实例。 I am facing compiler errors in doing so. 我在这样做时面临编译器错误。 Error for this code snippet is: 此代码段的错误是:

'required' initializer 'init(arrayLiteral:)' must be provided by subclass of 'NSSet'. “必需”初始化程序“ init(arrayLiteral :)”必须由“ NSSet”的子类提供。

Is there any way to get instances of 'Cat' and 'Dog'? 有没有办法获取“猫”和“狗”的实例?

Let's define a noise method on Animal for testing: 让我们在Animal上定义一种noise方法进行测试:

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

Also, let's use a normal Swift array to hold the class list: 另外,让我们使用普通的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)
}

Then, when we find a class that conforms to Animal , let's convert it to the appropriate Swift type ( (NSObject & Animal).Type ) so that when we instantiate it, we get an object of the appropriate type ( NSObject & Animal ): 然后,当我们找到一个符合Animal的类时,我们将其转换为适当的Swift类型( (NSObject & Animal).Type ),以便在实例化它时,得到一个适当类型的对象( NSObject & 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())
    }
}

Output: 输出:

woof
meow

Side note. 边注。 You might think you can avoid using class_conformsToProtocol by doing this: 您可能认为可以通过执行以下操作避免使用class_conformsToProtocol

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

But you will crash at runtime: 但是您将在运行时崩溃:

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

Under the covers, the as? 在幕后, as? test sends the conformsToProtocol: message to aClass using normal Objective-C messaging. 测试使用常规的Objective-C消息发送aClass conformsToProtocol:消息给aClass But there are various “zombie classes” in the system frameworks that crash when any message is sent to them. 但是,系统框架中存在各种“僵尸类”,当有任何消息发送给它们时,它们就会崩溃。 These classes are used to detect use-after-free errors. 这些类用于检测释放后使用错误。 The class_conformsToProtocol function doesn't use Objective-C messaging, so it avoids these crashes. class_conformsToProtocol函数不使用Objective-C消息传递,因此可以避免这些崩溃。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM