【问题标题】:How to call an Objective-C singleton from Swift?如何从 Swift 调用 Objective-C 单例?
【发布时间】:2014-06-10 18:35:08
【问题描述】:

我有一个如下的 Objective-C 单例:

 @interface MyModel : NSObject
    + (MyModel*)   model;
    ...


        + (MyModel*) model 
        {
            static MyModel     *singlton = nil;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^ {
                singlton = [[MyModel alloc] initSharedInstance];
            });
            return singlton;
        }


        - (MyModel*) initSharedInstance
        {
            self = [super init];
            if (self)
            etc.
        }

在 GUI 代码中的多个位置调用如下:

[[MyModel model] someMethod];

因此,模型将作为 GUI 的任何部分碰巧首先引用它的结果而创建。

我不确定如何在 Swift 中实现通过 [[MyModel model] someMethod] 访问类的等效方法,因为使用 Swift 的所有示例都涉及使用初始化程序创建对象以及何时将 Objective C 类方法代码转换为当方法没有参数时,Swift初始化程序代码存在问题。

【问题讨论】:

  • 您应该能够以MyModel.model 的身份访问它并获取单例对象。建议你把id改成instancetype
  • 如果我这样做“让 m = MyModel.model”,我会收到一个编译警告。它说:“模型不可用,使用对象构造 MyModel()”
  • 不管是 MyModel.model 还是 MyModel.model() 都是一样的编译错误
  • @Aminoacids:我认为您的问题是该方法看起来像一个方便的构造函数(沿着 +[NSArray array] 的行),这导致 Swift 对其进行特殊处理。尝试将其命名为更单例的名称,例如 sharedModel。 (目前无法对此进行测试,但我认为就是这样。)
  • @Chuck,是的,我在 H 先生的更新中将其重命名为单例,这很好

标签: objective-c swift


【解决方案1】:

更新 ++++++++++

仅当您使用从类名的后缀派生的名称来命名单例方法时才需要以下解决方法,即 OP 质疑方法名称是模型并且类称为 MyModel。

如果方法被重命名为单例,那么就可以像这样从 Swift 中调用它:

  let m  = MyModel.singleton()

+++++++++++++

我不知道这是否是好/坏的做法,但我能够通过添加一个虚拟 init 方法来解决在没有参数时初始化程序转换不起作用的问题。所以以其他答案中的代码为例:

@interface XYZThing : NSObject
+ (XYZThing*)  thing;
+ (XYZThing*)  thingWithFoo:(int)foo bar:(int)bar;
@end

@implementation XYZThing
+ (XYZThing*) thing
{
    NSLog(@"This is not executed");
    return nil;
}

+ (XYZThing*)thingWithFoo:(int)foo bar:(int)bar
{
    NSLog(@"But this is");
    return nil;
}
@end


...

let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)

上面这段代码不会调用thing方法,但会调用thingWithFoo:bar:方法。

但是如果它被改成这个,那么现在会调用 thing 方法:

    @interface XYZThing : NSObject
    + (XYZThing*)  init;
    + (XYZThing*)  thing;
    + (XYZThing*)  thingWithFoo:(int)foo bar:(int)bar;
    @end


    @implementation XYZThing

    + (XYZThing*) init
    {
         return nil;
    }
    + (XYZThing*) thing
    {
        NSLog(@"Now this is executed");
        return nil;
    }

    + (XYZThing*)thingWithFoo:(int)foo bar:(int)bar
    {
        NSLog(@"And so is this");
        return nil;
    }
    @end


...

    let thing = XYZThing()
    let otherThing = XYZThing(foo:3, bar:7)

【讨论】:

  • 我不知道具体原因,但singleton 方法对我有用。
  • "let m = MyModel.singleton() as AnyObject" 这对我有用。
  • 如果您尝试访问的属性是自定义类对象,您还应该将该自定义类添加到桥接头以显示。
【解决方案2】:

如果 Swift 编译器错误地将方法识别为类工厂方法,您可以使用 NS_SWIFT_NAME 宏,传递该方法的 Swift 签名以使其正确导入。例如:

+ (id)recordWithQuality:(double)quality NS_SWIFT_NAME(record(quality:));

所以,你的方法应该是这样的:

+ (MyModel*)model NS_SWIFT_NAME(log());

【讨论】:

    【解决方案3】:

    完全按照编译器警告的指示去做:

    MyModel().someMethod()
    

    继续阅读以了解原因...


    Swift 自动识别初始化器和便利构造器的 ObjC 约定。如果你有一个看起来像这样的类:

    @interface XYZThing : NSObject
    + (instancetype)thing;
    + (instancetype)thingWithFoo:(int)foo bar:(int)bar;
    @end
    

    ...然后,当 Swift 将它们转换为初始化程序时,它会省略作为类的通用名称的方法名称部分(Thing/thing),移动选择器中引用的部分参数作为参数标签,并删除连接这些部分的任何介词。所以初始化器声明在 Swift 中是这样的:

    class XYZThing: NSObject [
        init()
        init(foo: Int, bar: Int)
    }
    

    然后你像这样构造对象:

    let thing = XYZThing()
    let otherThing = XYZThing(foo:3, bar:7)
    

    后续:因为像 +[XYZThing thing] 这样的类方法在 ObjC 到 Swift 的翻译器中被视为初始化器(即使现在这似乎不能完全工作),这种命名模式对于单例来说是个坏主意。单例检索方法不应该是初始化器,因为初始化器总是会创建一个新实例。

    单例检索方法的名称应该不以类的通用名称开头;例如+sharedThing+defaultThing+oneThingToRuleThemAll

    【讨论】:

    • 谢谢,但是如果没有参数,将类方法转换为初始化程序的过程不起作用。因此,在您的示例中,所有内容都将编译,但不会调用 thing 而是调用 thingWithFoo:bar: 。因此我仍然认为这个问题没有答案,因为不希望在模型方法中添加一个虚拟参数,从而破坏许多使用它的现有 Obj-C 代码。
    • 这是另一个问题,在答案中提到了空类方法问题,我在尝试您的代码时也遇到过。 stackoverflow.com/questions/24093693/…
    • 我最初并没有意识到这是一种单例检索方法。这可能与您看到的异常行为有关(编译器坚持使用构造函数语法,但这不起作用)。由于这不是初始化程序,因此您不应将其命名为一个(请参阅编辑后的答案)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    相关资源
    最近更新 更多