-1

In Objective-C one can make a singleton that does not have a sharedInstance or similar class call simply by making the -init method reference the status singleton variable, like so

static MyObject *sharedObject;

/*
 * The init will return the actual singleton instance if called directly. 
 * The first time called it will create it and intialize it.
 */
- (instancetype)init
{
    static dispatch_once_t once;

    dispatch_once(&once, ^{
        id myself = [super init];
        if (nil != myself) {
            [self initialize];
            sharedObject = myself;
        }
    });

    return sharedObject;
}

So a user could call this MyObject *myObject = [[MyObject alloc] init]; as many times as he wanted and would get the same object back each time. But it is not obviously, from syntax, a singleton.

I am trying to get a similar functionality in Swift, where I can return the same object each time (an NSObject subclass) but so that it is not obviously a singleton.

I would call it var myObject = MyObject() or when bridging to Objective-C as above but they would all reference the same object.

I am familiar with the normal sharedInstance method of singleton in Swift.

Suggestions on how to do this would be appreciated.

This is not the same as the dispatch_once in Swift answers as that still uses a sharedInstance

chadbag
  • 1,837
  • 2
  • 20
  • 34
  • Possible duplicate of [Using a dispatch\_once singleton model in Swift](https://stackoverflow.com/questions/24024549/using-a-dispatch-once-singleton-model-in-swift) – Dávid Pásztor Aug 28 '17 at 22:29
  • 1
    This isn't possible in Swift since an initialiser implicitly returns `self` (or `nil` if a failable initialiser fails) – Paulw11 Aug 28 '17 at 22:31
  • 3
    Even though this was/is possible in Objective-C, you **shouldn't**. Init has a very well understood, common meaning. Changing it to instead implement singleton functionality is an unexpected diversion from the norm. – Alexander Aug 28 '17 at 22:53
  • @Alexander I would disagree to a certain extent. There are lots of cases where you can return other than the original self. All that is expected of alloc/init is that you get a nil or an object of the correct type back. (My needs now are mostly academic in terms of trying to figure it out vs actually having a case where it should be this versus the standard singleton pattern) – chadbag Aug 28 '17 at 22:55
  • 1
    It doesn't matter if a singleton looks like a singleton or not: If your code depends on them, it isn't good code. – vikingosegundo Aug 28 '17 at 22:56
  • @vikingosegundo It's not about depending on it having the characteristics of a singleton. The issue is that it's unnecessarily changing the semantics of initialization. For one, this causes an unnecessary allocation (calling `alloc`, only to have the `init` immediately discard its memory). – Alexander Aug 28 '17 at 23:17
  • @chadbag Yeah, such cases are class clusters, but you're still generally assigning the meaning of "create a new thing of this (sub)type" to initialization. But this is different – Alexander Aug 28 '17 at 23:18
  • @chadbag The singleton pattern is well known and identifiable by its trademark `[MyClass sharedInstance]` type syntax. Writing it so that `[[MyClass alloc] init]` does the same thing is **possible**, but it's a deviation from what's expected, without any real justification. It's not shorter, it's not faster, etc. – Alexander Aug 28 '17 at 23:20
  • One use is where the user of the code does not need to know and does not care that there is a single instance managing whatever it is he is doing. He just gets a new object of type `MyClass` and runs with it. The implementor of that functionality may want to only ever allow a single instance of it (for efficiency, or ease of implementation, etc.) but the user/soncumer does not need to know or care. I am not talking about mutable objects but rather controllers that allow access to immutable data. Again, mostly academic vs practical. – chadbag Aug 28 '17 at 23:26
  • The only time I ever did it in live code was when I provided the normal "singleton" *sharedInstance* but put the dispatch_once in the `-init` so the if someone used alloc/init they would get the same instance as was retrieved through the *sharedInstance*. Right now I am just thinking through some problems and the best way to structure them where the code does not care that it is a singleton, but the underlying implementation is easier as it provides access to common functionality that needs to be coordinated -- with the goal of allowing easy mocks for testing. – chadbag Aug 28 '17 at 23:29
  • @Dávid Pásztor It is not a duplicate. That stills uses the *sharedInstance* type solution. – chadbag Aug 29 '17 at 00:05
  • @chadbag I didn't down vote, but your question has a bit of "can't take no as an answer" vibe to it. This *can't* be done in Swift, because Swift has made `init` conventions into language rules. E.g. initializers must initialize all members. This used to be a convention in ObjC, but is now required and compiler-enforced in Swift. This precludes the ability to implement class clusters or a singleton as you show it. Initializers... initialize. If you want more complex behaviour then that, you have static/class vars/funcs at your disposal – Alexander Aug 29 '17 at 00:10
  • @Alexander Sorry it came across that way. My previous comments were related to the Objective-C variant that you had commented on and were exclusive to that variant and your comments on it. I was not trying to be obstinate about the Swift variation, which as has been pointed out, is not doable in the way I was looking for. – chadbag Aug 29 '17 at 00:28
  • @chadbag TIL the word "obstinate". I like it! – Alexander Aug 29 '17 at 00:46
  • @Paulw11 If you make an answer with this I will accept it. – chadbag Aug 29 '17 at 01:06
  • @chadbag if you actually took the time to look at the answers, you could see that it provides several solutions, including an Obj-C style one. – Dávid Pásztor Aug 29 '17 at 08:49
  • @Dávid Pásztor I did look at the answers. I did not see one that behaved the way I was asking for. The fact that it has *dispatch_once* in it does not mean it is related to what I was trying to do. Thanks! – chadbag Aug 29 '17 at 15:21

2 Answers2

1

I think you can do something similar to what you're looking for using Objective-C's associated objects. You can see a blog post about how to use it in Swift here: http://en.swifter.tips/associated-object/

I don't really understand the purpose of this though, necessarily- I think it would be desirable to make a singleton look like a singleton.

You can technically use these associated objects to create a function that always returns the same associated object, eg, func giveMeTheSameObjectEveryTime() -> AssociatedObjectType, which would be similar in syntax to init() -> AssociatedObjectType, but I think you're getting a similar effect to a singleton, since you'll have to create some boilerplate variables to hold the association, which is quite a bit more work than a simple static let sharedInstance property.

Daniel Williams
  • 256
  • 1
  • 12
1

In Objective-C an initialiser is just like any other method that is called on an instance

 [[alloc SomeClass] init]

You first alloc an instance and then explicitly invoke its initialiser.

init is able to return any object; returning self is just a convention that can be ignored in special cases, such as the one you have shown.

In Swift, init is special. An initialiser is invoked implicitly as part of the allocation

let x = SomeClass()

A Swift init implicitly returns self (or it can return nil in the case of failable initialiser that has failed). As a result, you cannot implement a "hidden singleton" in Swift by returning some other object.

Paulw11
  • 108,386
  • 14
  • 159
  • 186