【问题标题】:Accessing Swift protocol property implemented in an Obj-C class from another Swift class从另一个 Swift 类访问在 Obj-C 类中实现的 Swift 协议属性
【发布时间】:2019-01-23 19:39:35
【问题描述】:

我已经看到很多关于在 Swift 中实现 Obj-C 协议的问题,但反之则不然,而且我还没有具体看到这一点。

我正在使用混合的 Obj-C / Swift 代码库。我有一个定义如下的 Swift 协议:

NamedItem.swift

@objc protocol NamedItem {
    var name: String { get }
}

我有一个现有的 Objective-C 类,它目前有自己的 name 属性:

MyObjcClass.h

@interface MyObjcClass : NSObject
@property (nonatomic, strong, readonly) NSString* name;
@end

我有几个其他类具有name 属性,所以显然我想将它们都与协议相关联,而不是类型转换为一堆不同的类型。现在,如果我尝试将我的 Obj-C 类从拥有自己的属性切换到实现 Swift 协议:

MyObjcClass.h

@protocol MyObjcProtocol
@property (nonatomic, strong, readonly) NSString* place;
@end

@interface MyObjcClass : NSObject
@end

MyObjcClass.m

@interface MyObjcClass () <NamedItem>
@end

@implementation MyObjcClass
@synthesize name = _name;
@synthesize place = _place;
@end

这很好用,在我的其他 Objective-C 类中,但是如果我尝试从 Swift 类中访问名称属性

SomeSwiftClass.swift

let myObj = MyObjcClass()
myObj.name // error
myObj.place // no problem

我收到以下错误:

“MyObjcClass”类型的值没有成员“name”

如果我不从MyObjcClass.h 中删除现有的@property 声明并省略@synthesize 声明,一切都会正确构建。这似乎很奇怪和错误 - 如果您从 Obj-C 类中采用 Objc-C 协议,则不必重新定义属性,只需 @synthesize 语句就足够了。

我已经尝试以我能想到的所有方式定义 Swift 协议,并且能够找到有关的建议,但我无法解决这个问题。

那么,在 Objective-C 类中实现 Swift 协议属性(可能特别是只读属性?)以使另一个 Swift 类可以访问它的正确方法是什么?我 真的 必须在 Obj-C 标头中重新声明该属性吗?我知道我总是可以放弃它并在 Objective-C 中定义协议,但是...... Swift 是未来! (或类似的东西......)

【问题讨论】:

    标签: objective-c swift compiler-errors swift-protocols objective-c-swift-bridge


    【解决方案1】:
    // MyObjcClass.m
    
    @interface MyObjcClass () <NamedItem>
    @end
    

    这个声明对 Swift 是不可见的。将协议一致性子句&lt;NamedItem&gt; 移动到类的头文件中。

    // MyObjCClass.h
    
    // Required for visibility of the protocol
    #import "MyProject-Swift.h"
    
    @interface MyObjcClass : NSObject <NamedItem>
    @end
    

    正如您评论的那样,Swift 编译器仍然说myObj 没有属性name。这很奇怪。请注意

    let myObj = MyObjcClass() as NamedItem
    myObj.name
    

    编译正常。

    【讨论】:

    • 不。我已经尝试过了,不幸的是这并没有改变任何东西。让你得到的只是一个clang警告“找不到'MySwiftProtocol'的协议定义”,如果你#import“-Swift.h”(你不应该在标题中这样做),警告就会消失,但原来的错误仍然存​​在。
    • 将 MyProject-Swift.h 头文件导入到类头文件中。
    • 好的,找到了解决方案。你的MyObjcClass() as NamedItem 是关键线索。因此,首先,您在技术上可以转发声明协议以确保一致性(这是警告而不是错误),但我当然不喜欢这样。我找到了这篇文章(cjwirth.com/tech/circular-references-swift-objc)。作者在我使用的底部附近提供了一个聪明(不完美)的解决方案。
    • “感觉还是一个 Swift 错误” 同意。
    • 您应该随时发布您的完整解决方案作为自己的答案,顺便说一句。
    【解决方案2】:

    好的,通过 JoshCaswellthis 博客文章的一些建议,设法获得了一个可行的解决方案。

    类现在如下所示:

    NamedItem.swift

    @objc protocol NamedObject {
        var name: String { get }
    }
    

    MyObjcClass.h

    @protocol NamedItem;
    
    @interface MyObjcClass : NSObject
    - (id<NamedItem>)asNamedItem;
    @end
    

    MyObjcClass.m

    @interface MyObjcClass () <NamedItem>
    @synthesize name = _name;
    
    - (id<NamedItem>)asNamedItem
    {
        return self;
    }
    @end
    

    现在的用法如下:

    SomeSwiftClass.swift

    let myObj = MyObjcClass()
    myObj.asNamedItem.name
    

    不像我想要的那样干净利落,但它比一堆警告或承认失败并在 Objective-C 中重写协议要好。 (我不知道,也许不是……但我就是这么做的)。

    希望对其他人有所帮助。

    【讨论】:

    • 有趣,感谢您发布您的想法! (如果您愿意,请不要犹豫,将其标记为已接受的答案。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 2015-06-16
    • 2015-06-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多