Создать объект класса Objective C во время выполнения в Swift, который соответствует протоколу Objective C - PullRequest
0 голосов
/ 11 января 2019

У меня есть реализация протокола и интерфейса 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'. Этот код в быстрой:

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. Я сталкиваюсь с ошибками компилятора при этом. Ошибка для этого фрагмента кода:

'обязательный' инициализатор 'init (arrayLiteral :)' должен быть предоставлен подклассом 'NSSet'.

Есть ли способ получить экземпляры 'Cat' и 'Dog'?

1 Ответ

0 голосов
/ 11 января 2019

Давайте определим метод noise на Animal для тестирования:

@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 & 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())
    }
}

Выход:

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? отправляет сообщение conformsToProtocol: на aClass с использованием обычного обмена сообщениями Objective-C. Но в системных рамках существуют различные «классы зомби», которые вылетают при отправке им любого сообщения. Эти классы используются для обнаружения ошибок использования после освобождения. Функция class_conformsToProtocol не использует обмен сообщениями Objective C, поэтому она предотвращает эти сбои.

...