从Objective-C调用Swift单例

25

我在尝试从Objective-C中访问一个Swift Singleton,但是遇到了一些问题。

@objc class SingletonTest: NSObject {

    // swiftSharedInstance is not accessible from ObjC
    class var swiftSharedInstance: SingletonTest {
    struct Singleton {
        static let instance = SingletonTest()
        }
        return Singleton.instance
    }        
}

无法访问swiftSharedInstance。

8个回答

27

Nicky Goethlis的答案是正确的,但我想要补充一种用Swift创建单例的另一种方法,称为一行代码单例。最近我刚刚发现了这种方法,它不使用Struct

Singleton.swift

@objc class Singleton: NSObject {

  static let _singletonInstance = Singleton()
  private override init() {
    //This prevents others from using the default '()' initializer for this class.
  }

  // the sharedInstance class method can be reached from ObjC. (From OP's answer.)
  class func sharedInstance() -> Singleton {
    return Singleton._singletonInstance
  }

  // Some testing
  func testTheSingleton() -> String {
    return "Hello World"
  }
}

一些ObjC文件.m

Singleton *singleton = [Singleton sharedInstance];
NSString *testing = [singleton testTheSingleton];
NSLog(@"Testing---> %@",testing);

这种方法对我来说不起作用,并且在从ObjC文件访问时导致应用程序崩溃(在您的答案中,这一行:Singleton *signleton = [Singleton sharedInstance];)。 - Yuvrajsinh
8
在Swift 4.1中,你甚至不需要使用sharedInstance()方法了。你只需要加上@objc注解在静态的'singletonInstance'属性上,在ObjC代码中就可以直接访问该属性。(这可能早在4.1之前就开始了;我今天才尝试玩玩。) - Darren Black
@DarrenBlack 是正确的,但需要标记为公共的。 - heqingbao

13

Swift 5 及以上版本

final class Singleton: NSObject {

    @objc static let shared = Singleton()

    @objc var string: String = "Hello World"

    private override init() {}   
}

Objective-C中使用

#import <ProjectName-Swift.h> // change ProjectName to actual project name 

NSLog("Singleton String = %@", [Singleton shared].string);

在你的 Objective-C 文件中添加导入语句 #import <ProjectName-Swift.h> - Suhit Patil
请参考以下链接了解有关将Swift导入Objective-C的信息:https://developer.apple.com/documentation/swift/importing-swift-into-objective-c - Suhit Patil

9

目前我有以下解决方案。也许我忽略了某些东西,使我能够直接访问“swiftSharedInstance”?

@objc class SingletonTest: NSObject {

    // swiftSharedInstance is not accessible from ObjC
    class var swiftSharedInstance: SingletonTest {
    struct Singleton {
        static let instance = SingletonTest()
        }
        return Singleton.instance
    }

    // the sharedInstance class method can be reached from ObjC
    class func sharedInstance() -> SingletonTest {
        return SingletonTest.swiftSharedInstance
    }

    // Some testing
    func testTheSingleton() -> String {
        return "Hello World"
    }

}

那么在 ObjC 中,我可以在导入 xcode 生成的 Swift 头文件后获取 sharedInstance 类方法。

SingletonTest *aTest = [SingletonTest sharedInstance];
NSLog(@"Singleton says: %@", [aTest testTheSingleton]);

2
尝试在类变量声明前面添加@objc,看看会出现什么错误。 - Jack
你必须添加共享实例方法的主要原因是因为你不能在Objective-C中使用Swift结构体。 - Shial
@Jack 在类变量上使用@objc注释对我也有效。 - Darren Black
1
我不需要一个包装结构体或者一个sharedInstance()方法,我可以通过Objective-C直接访问类/静态变量。 - Darren Black

2
为了让SingletonTest类的成员(其中swiftSharedInstance是该类的成员)可访问,可以在类上使用@objcMembers修饰符,或直接在swiftSharedInstance上添加@objc修饰符:
@objc @objcMembers class SingletonTest: NSObject {

    // swiftSharedInstance is not accessible from ObjC
    class var swiftSharedInstance: SingletonTest {
        struct Singleton {
            static let instance = SingletonTest()
        }
        return Singleton.instance
    }        
}

或者:

@objc class SingletonTest: NSObject {

    // swiftSharedInstance is not accessible from ObjC
    @objc class var swiftSharedInstance: SingletonTest {
        struct Singleton {
            static let instance = SingletonTest()
        }
        return Singleton.instance
    }        
}

1
我以前不知道 @objcMembers 能有用!但是,我不确定是否会影响桥接性能。 - Antoine Rucquoy

2
创建完“桥接头文件”后,请确保在您的应用程序目标的“构建设置”中设置了“Objective-C生成的接口头文件名称”。如果该值为空,请添加以下值: $(SWIFT_MODULE_NAME)-Bridging-Header.h
$(SWIFT_MODULE_NAME)-Swift.h

你需要在你的单例中添加 @objc property wrapper:
@objc final class Singleton: NSObject {

    @objc static let sharedInstance = Singleton()

    @objc func foo() { }
}

然后,在Objective-Cclass中,导入以下内容:

// Replace the "App" with your Target name.
#import "App-Swift.h"

最后,在编译项目之后,您可以在Objective-Cclass中使用从Swift导入的singleton

[[Singleton sharedInstance]foo];

0

更新于2022年10月12日

ObcMember,这样您就不必在每个函数后面写objC了。

Swift类

@objcMembers class SwiftHelpingExtentions: NSObject {

    static let instanceShared = SwiftHelpingExtentions()
        func testingMethod() {     
            print("testing")    
        }
    }
}

关于 Objective-C 视图控制器

  1. 导入:#import "App-Swift.h"

  2. 调用方法:[SwiftHelpingExtentions.instanceShared testingMethod];


0

你已经基本掌握了。要在 Obj-C 中使用 Swift 类,你需要导入生成的头文件 #import "SingletonTest-Swift.h" 或者使用前向声明 @class MySwiftClass

此外,该类需要继承自 Obj-C 类,就像你在这里使用 NSObject 一样,或者 标记为 @objc 以公开它。不过,你不需要同时执行这两个操作,@objc 是更细粒度的选项,用于选择要公开的内容。

苹果公司有一些很好的文档,介绍了所有这些内容,还有两个不同的WWDC会议,可以观看关于 Obj-C 互操作性的主题。


代码的问题在于实例没有被暴露出来。请查看我的答案,可能会有解决方案。 - ekinsol

0
不要忘记将sharedInstance设置为public
public final class TestSwiftMain: NSObject {
    @objc public static let sharedInstance = TestSwiftMain()
    private override init() {}

    @objc public func test() {
        print("testing swift framework")
    }
}

在 Objc 中使用它

[[testSwiftMain sharedInstance] test];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接