从swift访问向前声明的枚举的属性

Mir*_*vac 14 objective-c ios swift objc-bridging-header

鉴于存在一个用Swift编写的ObjC兼容枚举:

// from MessageType.swift
@objc enum MessageType: Int {
    case one
    case two
}
Run Code Online (Sandbox Code Playgroud)

和一个MessageType具有必须向前声明的类型属性的ObjC类:

// from Message.h
typedef NS_ENUM(NSInteger, MessageType);

@interface Message: NSObject
@property (nonatomic, readonly) MessageType messageType;
@end
Run Code Online (Sandbox Code Playgroud)

为了使用MessageSwift代码库的其余部分,将Message.h其添加到桥接头中:

// from App-Bridging-Header.h
#import "Message.h"
Run Code Online (Sandbox Code Playgroud)

现在,假设有一个Swift类试图读取messageType属性:

// from MessageTypeReader.swift
class MessageTypeReader {
    static func readMessageType(of message: Message) -> MessageType {
        return message.messageType
    }
}
Run Code Online (Sandbox Code Playgroud)

编译将失败,并出现以下错误:

Value of type 'Message' has no member 'messageType'
Run Code Online (Sandbox Code Playgroud)

我的问题是:有没有办法向前声明一个Swift枚举,以便MessageTypeReader能够访问该属性?

注意:我知道可以将消息重写为Swift或将App-Bridging-Header.h导入到Message.h中,但这不是一个选项,我正在寻找一种适用于当前设置的解决方案.

Ste*_*cht 12

我想在Objective-C端使用NS_ENUM的一个原因是编译时间检查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
Run Code Online (Sandbox Code Playgroud)

所以基本的想法是:

Swift将C联盟作为Swift结构导入.尽管Swift不支持本机声明的联合,但作为Swift结构导入的C联合仍然表现得像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
Run Code Online (Sandbox Code Playgroud)

在Swift Side上使用

由于messageType.swift属性最初来自Swift端(请参阅MessageType的定义),因此我们可以安全地使用force-unwrap.

class MessageTypeReader {

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

}
Run Code Online (Sandbox Code Playgroud)


iel*_*ani 6

这里是一个解决方法建议通过Cristik(全部归功于它们):

  • Message.h,声明messageTypeNSInteger:

    @interface Message : NSObject
    @property (nonatomic, readonly) NSInteger messageType;
    @end
    
    Run Code Online (Sandbox Code Playgroud)

    Apple NS_REFINED_FOR_SWIFT 建议使用,但这里不需要.

  • 在Swift中,添加以下Message扩展名:

    extension Message {
        var messageType: MessageType {
            guard let type = MessageType(rawValue: self.__messageType) else {
                fatalError("Wrong type")
            }
            return type
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)