如何在同一框架内访问 Objective-C 中的内部 Swift 类?

Posted

技术标签:

【中文标题】如何在同一框架内访问 Objective-C 中的内部 Swift 类?【英文标题】:How to access an internal Swift class in Objective-C within the same framework? 【发布时间】:2015-12-18 10:33:26 【问题描述】:

使用混合框架。在 Obj-C 文件中导入,但内部类不可见,只有公共类。

文档明确指出 Swift 和 Obj-C 之间应该可以使用内部类:

将 Swift 导入到 Objective-C 要将一组 Swift 文件导入到与 Objective-C 代码相同的框架目标中,您不需要 需要将任何内容导入框架的伞头文件中。 相反,为您的 Swift 代码导入 Xcode 生成的头文件 到任何你想从中使用 Swift 代码的 Objective-C .m 文件中。 因为为框架目标生成的标头是 框架的公共接口,只有标有公共的声明 修饰符出现在为框架目标生成的标头中。 你 仍然可以使用标有 来自框架的 Objective-C 部分的内部修饰符, 只要它们是在继承自 Objective-C 类。有关访问级别修饰符的更多信息,请参阅 Access Control 在The Swift Programming Language (Swift 2)。

代码示例(使用框架创建新项目)

// SwiftObject.swift

public class SwiftObject: NSObject 
    public class func doSomething() 


internal class YetAnotherSwiftObject: NSObject 
    internal class func doSomething() 


// SomeObject.m file

@implementation SomeObject

- (void)someMethod 
    [SwiftObject doSomething];


- (void)someOtherMethod 
    [YetAnotherSwiftObject doSomething]; // Use of undeclared identifier


@end

【问题讨论】:

当前文档developer.apple.com/documentation/swift/… 说 使用 internal 修饰符标记并在继承自 Objective-C 类的类中声明的方法和属性可供 Objective-C 运行时访问。 但是,它们在编译时不可访问,并且不会出现在为框架目标生成的标头中。 【参考方案1】:

如文档中所示,标有internal 修饰符的声明不会出现在生成的标头中,因此编译器不知道它们,因此会抱怨。当然,您可以使用performSelector 方法发送消息,但这不方便且容易出错。我们只需要帮助编译器知道那些声明就在那里。

首先,我们需要使用@objc 属性变体,它允许您在Objective-C 中为符号指定名称:

// SwiftObject.swift

@objc(SWIFTYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject 
    internal class func doSomething() 

然后您只需要使用您想在代码中使用的方法创建 @interface 声明 - 这样编译器会很高兴,并且还使用您之前指定的符号名称应用 SWIFT_CLASS 宏 - 所以链接器会选择实际的实现:

// SomeObject.m file

SWIFT_CLASS("SWIFTYetAnotherSwiftObject")
@interface YetAnotherSwiftObject : NSObject

+ (void)doSomething;

@end


@implementation SomeObject

- (void)someOtherMethod 
    [YetAnotherSwiftObject doSomething]; // Should work now !!!


@end
为了清楚起见,我在 .m 文件中使用了接口声明,更好的选择是将此类声明合并到 .h 文件中并包含它。 通过在该接口中声明方法,我们向编译器做出承诺,如果您将不存在的方法(或签名错误等)放在那里,它不会抱怨。显然,您会在这种情况下会在运行时崩溃 - 所以要小心。

【讨论】:

不错!我不知道那个宏,谷歌没有显示太多。你有任何来自 Apple 文档的参考资料吗? 不错吗?这是非常冗长和混乱的。这真的是你需要做的吗?我遇到了同样的问题,甚至苹果也应该设法做比这更好的事情。我要放弃这个,回到目标c,这太不合理了! +1 用于发布实际有效的内容(经过数小时的搜索)。 根据 Apple 文档“您仍然可以在框架的 Objective-C 部分中使用标记为 internal 修饰符的 Swift 方法和属性,只要它们是在继承自的类中声明的一个 Objective-C 类”,如果 swift 类派生自另一个 objc 类,它应该在 objc 代码中可用。我有类似的问题,我用 @objc 标记了我的 swift 类,它继承自 NSObject 但在 objc 代码中仍然不可用。【参考方案2】:

对我来说,它只是通过检查:“仅允许应用扩展 API”来工作。您可以通过转到项目设置找到它,选择您的目标,然后它位于部署信息下的常规选项卡中。

谁能给我解释一下,为什么这能解决问题?

【讨论】:

我爱你!哈哈 。这是地球上唯一找到绕过这个愚蠢限制的答案的地方。不应要求任何框架将其 Swift 类公开给 API 使用者,只是为了能够与内部 ObjC 代码进行互操作! 小心尝试这个选项!它将在自动生成的标题中列出类和方法。仅当您不关心将其保密时才使用它。 但是勾选了这个,会有一堆Cocoa API缺失。【参考方案3】:

虽然上述解决方案有效 (https://***.com/a/33159964/5945317),但它似乎过于复杂和不直观:

很复杂,因为它似乎添加了不必要的东西 - 我将在下面提供一个更流畅的解决方案。 不直观,因为 objc 宏 SWIFT_CLASS 解析为 SWIFT_RUNTIME_NAME,并且提供的值实际上不是运行时名称 - 标头中的 objc 类名称也不匹配 @objc 中的 Swift 属性参数。尽管如此,令人惊讶的是,该解决方案仍然有效——但对我来说,原因尚不清楚。

这是我们在自己的项目中测试过的,并且认为是更好的解决方案(使用上面的示例):

// YetAnotherSwiftObject.swift

@objc(OBJCPREFIXYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject 
    @objc internal class func doSomething() 

// OBJCPREFIXYetAnotherSwiftObject.h

@interface OBJCPREFIXYetAnotherSwiftObject : NSObject

+ (void)doSomething;

@end

就是这样。该接口看起来像一个常规的 objc 接口。这带来了额外的好处,您可以将其包含在其他头文件中(如果您使用 SWIFT_CLASS 宏,则无法执行此操作,因为它来自自动生成的 Swift 头文件,而您又无法将其包含在 objc 头文件中,因为循环依赖)。

在 Swift 方面,唯一相关的是您为类提供正确的 objc 名称。请注意,我只使用名称前缀来保持语言一致性——你甚至可以在任何地方使用YetAnotherSwiftObject(即,在 Swift 中的 objc 标头和 @objc 属性中——但无论如何你都需要使用显式命名来保留这个属性, 并且需要和头部中的类名保持一致)。

如果您正在逐步将 objc 框架转换为 Swift,这也会让您的生活更轻松。您只需像以前一样保留 objc 标头,现在在 Swift 中提供实现。

【讨论】:

【参考方案4】:

使用 internal 修饰符标记并在继承自 Objective-C 类的类中声明的方法和属性可供 Objective-C 运行时访问。

所以让我们利用它:

class MyInternalClass: NSObject 
    @objc var internalProperty = 42

@interface MyPublicClass()
@end

@implementation MyPublicClass

+ (void) printValue 

    Class myInternalClass = NSClassFromString(@"MyPackageNameIfAny.MyInternalClass");
    id myInternalClassInstance = [myInternalClass new];
    int value = [myInternalClassInstance performSelector:@selector(internalProperty)];

    NSLog(@"Value is %d ", value); // "value is 42"


@end

使用SWIFT_CLASS 宏和@objc 类属性在归档时很容易导致错误。这种方法更安全。

【讨论】:

以上是关于如何在同一框架内访问 Objective-C 中的内部 Swift 类?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Objective-C 中的 Speech 框架实现语音到文本?

框架方法可访问性中的桥接头

从 Swift 访问 Objective-C #define 常量

Objective-C:无法#import我的框架

在Intellij上开发项目发布到tomcat时,同一个局域网内的其他机子访问不到自己电脑上tomcat中的项目,只能本机访问

在组合项目中从Swift类访问Objective-C类中的singleton sharedInstance