使用Objective C类扩展Swift类

时间:2016-04-12 03:59:19

标签: objective-c swift interpolation

我处于需要使用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会产生编译错误。

3 个答案:

答案 0 :(得分:15)

我也一直在争论这个问题。不幸的是documentation非常明确地指出不允许这种模式:

  

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

     

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

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

  

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

     

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

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

/**
 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

答案 1 :(得分:3)

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。

答案 2 :(得分:2)

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