How to call an Objective-C singleton from Swift?

后端 未结 3 1860
没有蜡笔的小新
没有蜡笔的小新 2021-02-20 07:48

I have an objective-C singleton as follows:

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


        + (MyModel*) model 
        {
                     


        
相关标签:
3条回答
  • 2021-02-20 08:02

    Do exactly what the compiler warning tells you to:

    MyModel().someMethod()
    

    Read on to see why...


    Swift automatically recognizes ObjC conventions for initializers and convenience constructors. If you have a class that looks like this:

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

    ...then, when Swift turns them into initializers, it elides the part of the method name that's the generic name of the class (Thing/thing), moves the part of the selector that refers to the parameter to be a parameter label, and drops any prepositions connecting those parts. So the initializer declarations look like this in Swift:

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

    and you construct objects like this:

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

    A followup: because class methods like +[XYZThing thing] are treated like initializers by the ObjC to Swift translator (even if that doesn't seem to fully work right now), that naming pattern is a bad idea for singletons. A singleton retrieval method shouldn't be an initializer, because an initializer always creates a new instance.

    A singleton retrieval method should instead have a name that doesn't start with the generic name of the class; e.g. +sharedThing, +defaultThing, +oneThingToRuleThemAll, etc.

    0 讨论(0)
  • 2021-02-20 08:05

    UPDATE ++++++++++

    The workaround below is only necessary if you name your singleton method with a name derived from the suffix of the class name i.e. the OPs question the method name is model and the class is called MyModel.

    If the method is renamed to something like singleton then it is possible to call it from Swift just like this:

      let m  = MyModel.singleton()
    

    +++++++++++

    I don't know if this is good/bad practice but I was able to get around the problem with initializer conversion not working when there are no parameters by adding a dummy init method. So using the code from the other answer as an example:

    @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)
    

    With this code above the thing method is not called, but the thingWithFoo:bar: method is.

    But if it is changed to this then now the thing method will get called:

        @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)
    
    0 讨论(0)
  • 2021-02-20 08:15

    If the Swift compiler mistakenly identifies a method as a class factory method, you can use the NS_SWIFT_NAME macro, passing the Swift signature of the method to have it imported correctly. For example:

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

    so,your method should be this:

    + (MyModel*)model NS_SWIFT_NAME(log());
    
    0 讨论(0)
提交回复
热议问题