Давайте определим метод 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, поэтому она предотвращает эти сбои.