使用 Objective-C 类别扩展 Swift 类

Posted

技术标签:

【中文标题】使用 Objective-C 类别扩展 Swift 类【英文标题】:Extending a Swift class with Objective-C category 【发布时间】:2016-08-02 11:34:42 【问题描述】:

我需要使用 Objective-C 类别来扩展 Swift 类。我做了如下的事情:

在“SomeClass.swift”中:

class SomeClass: NSObject 

在“SomeClass+Extension.h”中:

#import "Project-Swift.h"
@interface SomeClass (Extension) 
-(void)someMethod();
@end

这运作良好。如果我尝试在我的 Objective C 代码中使用 SomeClass 扩展,那很好。

问题是,如果我想在另一个 Swift 类中使用someMethod(),我需要将SomeClass+Extension.h 文件放入我的ObjC-BridgingHeader.h 文件中。

但是这样做会导致循环依赖,因为SomeClass+Extension.h 也会导入Project-Swift.h

有人有解决这个问题的好方法吗?

请注意,简单地在类别标题中前向声明类是行不通的,因为类别不能将前向声明用于它自己的实现:

@class SomeClass 不导入Project-Swift.h 会产生编译错误。

【问题讨论】:

***.com/questions/27761407/… @TroyT 您发布的解决方案将不起作用,因为这是一个objective-C类别,在一个类别中您不能简单地转发声明一个类,您必须导入您扩展的类的头文件。 首先尝试了解@class 和#import 之间的区别,您可以在这里找到一个很好的答案***.com/questions/322597/class-vs-import @Johnykutty 我知道@class 和#import 之间的区别。但在这种情况下,@class 将不起作用,因为它是一个类别。在一个类别中,您必须显式导入您扩展的类的头文件,如果您尝试进行前向声明,您将获得class undefined error。所以很遗憾你的解决方案行不通。 即使它是一个类@class 也不起作用,因为@class 指令只告诉编译器它是一个类,没有其他信息,如它的属性、方法、超类等。如果你想使用他们你应该导入相应的头文件 【参考方案1】:

坏人

我也一直在努力解决这个问题。不幸的是,documentation 非常明确地声明这种模式是不允许的:

为避免循环引用,请勿将 Swift 代码导入 Objective-C 头文件 (.h) 文件。相反,您可以转发声明一个 Swift 在 Objective-C 接口中引用它的类或协议。

只能使用 Swift 类和协议的前向声明 作为方法和属性声明的类型。

在整个链接页面中,您会注意到它一直提到将生成的标头专门导入到 .m 文件中:

将 Swift 代码从同一目标导入到 Objective-C

将 Swift 代码从该目标导入任何 Objective-C .m 文件 在那个目标内

一个可能适合您的解决方案是创建一个快速扩展,重新定义您在该类别中需要的每个方法。它既脆弱又丑陋,但可以说是最干净的解决方案。

/**
 Add category methods from objc here (since circular references prohibit the ObjC extension file)
 */
extension SomeClass 
    @nonobjc func someMethod() 
        self.performSelector(Selector("someMethod"))
    

@noobjc 添加到前面允许 在不覆盖 ObjC 实现的情况下使用相同的方法签名 现在是来自桥接的import "SomeClass+Extension.h" 标题可以删除

如果需要支持两个以上的输入参数,或者需要更紧密的类型耦合,我建议使用运行时调用底层函数。一个很好的描述是here。

【讨论】:

似乎没有真正简单的方法可以做到这一点,但感谢您使用官方文档进行清理。接受正确答案+奖励赏金【参考方案2】:

从Interoperability guide,我们无法直接访问 .swift [SomeClass] 类的子类/分类/扩展 Objc 对象。

但作为一个转机,我们可以这样做:

对于变量,我们可以这样做:

extension Class 
    private struct AssociatedKeys 
        static var DescriptiveName = "sh_DescriptiveName"
    

    var descriptiveName: String? 
        get 
            return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
        

        set 
            if let newValue = newValue 
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.DescriptiveName,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            
        
    

对于Methods,我们可以使用method_swizzling,不推荐使用。

【讨论】:

【参考方案3】:

作为一种简单的解决方案,您可以将扩展移动到您的 Swift 代码中。这样你就不会有任何依赖问题了。

【讨论】:

这并不是我真正需要的,因为重点是重用大量的 Objective C 代码而不需要在 Swift 中重写。

以上是关于使用 Objective-C 类别扩展 Swift 类的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C 框架中的 Swift 类别在运行时无法识别

如何在 Swift 中调用 Objective-C 类别方法

Objective-C中结构的扩展

符合协议的 Swift 扩展

在 Swift 3 的值类型中访问 Objective-C 类别的属性(关联参考)

在 Swift 3 的值类型中访问 Objective-C 类别的属性(关联参考)