Доступ к собственности заранее объявленного enum от swift
Учитывая, что существует ObjC-совместимое перечисление, написанное на Swift:
// from MessageType.swift
@objc enum MessageType: Int {
case one
case two
}
и класс ObjC со свойством типа MessageType
который должен быть заранее объявлен:
// from Message.h
typedef NS_ENUM(NSInteger, MessageType);
@interface Message: NSObject
@property (nonatomic, readonly) MessageType messageType;
@end
Для того, чтобы использовать Message
в остальной части кодовой базы Swift, Message.h
был добавлен в заголовок моста:
// from App-Bridging-Header.h
#import "Message.h"
Теперь представьте, что есть класс Swift, который пытается прочитать messageType
имущество:
// from MessageTypeReader.swift
class MessageTypeReader {
static func readMessageType(of message: Message) -> MessageType {
return message.messageType
}
}
Компиляция завершится с ошибкой:
Value of type 'Message' has no member 'messageType'
Мой вопрос был бы: есть ли способ заранее объявить перечисление Swift для того, чтобы MessageTypeReader
чтобы иметь доступ к собственности?
Примечание: я знаю о возможности переписать Сообщение в Swift или импортировать App-Bridging-Header.h в Message.h, но здесь это не вариант, я ищу решение, которое работало бы с текущей настройкой.
2 ответа
Я предполагаю, что одной из причин использования NS_ENUM на стороне Objective-C является проверка времени компиляции, является ли использование оператора switch исчерпывающим.
Если это так, можно использовать C союзы.
Заголовок Objective-C
typedef NS_ENUM(NSInteger, MessageType);
union MessageTypeU {
MessageType objc;
NSInteger swift;
};
@interface Message : NSObject
@property (nonatomic, readonly) union MessageTypeU messageType;
@end
Итак, основная идея такова:
Swift импортирует C союзы как структуры Swift. Хотя Swift не поддерживает изначально объявленные объединения, объединение C, импортированное как структура Swift, по-прежнему ведет себя как объединение C.
...
Поскольку объединения в C используют один и тот же базовый адрес памяти для всех своих полей, все вычисленные свойства в объединении, импортированном Swift, используют одну и ту же базовую память. В результате изменение значения свойства в экземпляре импортированной структуры приводит к изменению значения всех других свойств, определенных этой структурой.
Пример реализации Objective-C
@interface Message ()
@property (nonatomic, readwrite) union MessageTypeU messageType;
@end
@implementation Message
- (instancetype)init
{
self = [super init];
if (self) {
_messageType.objc = MessageTypeTwo;
[self testExhaustiveCompilerCheck];
}
return self;
}
- (void)testExhaustiveCompilerCheck {
switch(self.messageType.objc) {
case MessageTypeOne:
NSLog(@"messageType.objc: one");
break;
case MessageTypeTwo:
NSLog(@"messageType.objc: two");
break;
}
}
@end
Использование на Swift Side
Поскольку свойство messageType.swift изначально происходит со стороны Swift (см. Определение MessageType), мы можем безопасно использовать принудительное развертывание.
class MessageTypeReader {
static func readMessageType(of message: Message) -> MessageType {
return MessageType(rawValue: message.messageType.swift)!
}
}
Вот обходной путь, предложенный Cristik (вся заслуга им):
В
Message.h
, объявитьmessageType
какNSInteger
:@interface Message : NSObject @property (nonatomic, readonly) NSInteger messageType; @end
С помощью
NS_REFINED_FOR_SWIFT
рекомендуется Apple, но не обязательно здесь.В Swift добавьте следующее
Message
расширение:extension Message { var messageType: MessageType { guard let type = MessageType(rawValue: self.__messageType) else { fatalError("Wrong type") } return type } }