问题
I have Objective-C Protocol and Interface implementation as below:
@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. 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'.
Is there any way to get instances of 'Cat' and 'Dog'?
回答1:
Let's define a noise
method on Animal
for testing:
@protocol Animal <NSObject>
- (NSString *)noise;
@end
Also, let's use a normal Swift array to hold the class list:
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
):
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:
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?
test sends the conformsToProtocol:
message to aClass
using normal Objective-C messaging. 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.
来源:https://stackoverflow.com/questions/54141572/create-object-of-objective-c-class-at-runtime-in-swift-which-conforms-to-object