Доступ к собственности заранее объявленного enum от swift - PullRequest
0 голосов
/ 06 декабря 2018

Учитывая, что существует совместимое с 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 ]

0 голосов
/ 09 декабря 2018

Я полагаю, что одной из причин использования 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, используют одну и ту же базовую память.В результате изменение значения свойства в экземпляре импортированной структуры изменяет значение всех других свойств, определенных этой структурой.

см. Здесь: https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_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

Поскольку свойство messageType.swift изначально происходит со стороны Swift (см.определение MessageType) мы можем безопасно использовать force-unwrap.

class MessageTypeReader {

    static func readMessageType(of message: Message) -> MessageType {
        return MessageType(rawValue: message.messageType.swift)!
    }

}
0 голосов
/ 09 декабря 2018

Вот обходной путь , предложенный от Cristik (вся заслуга им):

  • В Message.h, объявить messageType as 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
        }
    }
    
...