7

I'm new to writing singletons and I have to use one for a current iOS project. One of the requirements is that it can be killed. I know this goes against the design of a singleton, but is this something that should/could be done?

jscs
  • 63,694
  • 13
  • 151
  • 195
MattDice
  • 195
  • 3
  • 8
  • By definition, a singleton can never be allocated again and, therefore, the notion of deallocation doesn't make any sense. Tear down for termination? Sure. But you can't rely on that happening, regardless. – bbum Aug 14 '12 at 02:18
  • @bbum That depends on the definition. To me, the definitive definition is from the iconic GoF book: "Ensure a class only has one instance". That does not exclude destruction and reconstruction. – Nikolai Ruhe Aug 14 '12 at 02:35
  • 2
    By definition within Cocoa and iOS development, then. – bbum Aug 14 '12 at 04:18
  • @bbum Are you referring to some documentation or other commonly known definition? I'm just curious, because to me, singletons in Cocoa always were just that questionable piece from the Cocoa Fundamentals Guide (the one that mocks with retainCount and so). – Nikolai Ruhe Aug 15 '12 at 11:22
  • Convention over 20+ years of singleton implementations within the OS and across third party codebases, including the documentation supplied examples (which are overblown in complexity). – bbum Aug 15 '12 at 15:56
  • I'm late to the party but if you call up a singleton the right way with dispatch_once, this is pointless. – dokun1 Jun 17 '14 at 09:25

7 Answers7

8

Of course it could be done, but if you're looking for an object that can be created, and then released when not needed... that sounds like a regular object. :)


Generally singletons control their own lifecycles. You're going to get one-sided discussion here unless you say more about the two requirements (one, that you use a singleton, and two, that it can be released at will), and why they both make sense in your case.

It might be because the singleton wraps some other resource that is inherently unique (like a file resource or network connection). If this is true, then generally the singleton is the "manager" of that resource, and you'd expose control of that resource via the singleton's interface.

Or it might be because the singleton object holds on to a ton of memory (a buffer of some sort), and you want to be sure that's flushed as necessary. If this is the case, then you can be smarter about each of its methods creating and releasing memory as necessary, or you can have the singleton listen for the low memory system notifications and behave appropriately.

Essentially, I'd be hard pressed to construct a case where it really made sense for the singleton object itself to be released. A single basic object takes only a handful of bytes in memory, and hurts no one by hanging around.

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • I had to convert an existing app over to a static library and I had to convert all the AppDelegate logic into a Singleton for minimal code refactoring. The "library app" needs to be able be presented either by making it the rootviewcontroller of the window, or being presented/pushed by another viewcontroller. In the rootviewcontroller scenario the singleton would hold true to design and never be killed, the other 2 scenarios I think is why the requirement to be able to kill it. – MattDice Aug 14 '12 at 02:30
  • 3
    @MattDice: Understood, but you're better off designing around this. If the library's "AppDelegate" actually carries no state (properties), then you could convert all its methods into class methods with one mechanical search/replace. If it does carry state (and thus becomes effectively just a container for globals), then consider whether it's necessary to ever destroy it anyways-- maybe it's just fine to leave it around as you might any singleton. If you must reset state, then consider adding some sort of `-reset` method that restores its properties to a default state. – Ben Zotto Aug 14 '12 at 02:37
  • 1
    @MattDice: And bear in mind that if you could ever have more than one of these "library apps" alive on the navigation stack at the same time, then you can't even use a singleton if you want state kept for them independently; in this case you'd really need to refactor. – Ben Zotto Aug 14 '12 at 02:39
  • I like adding a reset method, kind of like a "soft kill". Thanks Matt – MattDice Aug 14 '12 at 02:47
  • Just saw 2nd comment. Great point. I know the current app that is going to implement the lib, this won't be a problem but that is a possible use case for sure. I'm thinking a regular instance is probably the better solution. – MattDice Aug 14 '12 at 02:51
6

I know this goes against the design of a singleton

It also goes against the usual memory management patten in Objective-C. Normally, an object gets to retain another object to prevent it from being destroyed, and to release it to allow the object to be destroyed. Explicitly destroying an object, though, isn't something that other objects get to do.

Consider what would happen if object A gets the shared instance S1 of a singleton class S. If A retains S1, S1 will continue to exist even if some class method releases S and sets the global variable that points to the shared instance to nil. When the class later creates a new shared instance, S2, there will be two instances of S, namely S1 and S2. This violates the property that defines the singleton in the first place.

You might be able to get around this problem by overriding -retain and maybe swizzling -release, but that seems like a lot of complexity to solve a problem that shouldn't exist in the first place.

A possible alternative is to reset your shared object instead of trying to destroy it. You could set all it's attributes to some known (possibly invalid) state if you want, and then have a class method the re-initializes the shared object. Just be aware of the effect of all that on any objects that might be using the shared object.

Caleb
  • 124,013
  • 19
  • 183
  • 272
6

Just about every singleton I've ever written (save for totally UI centric controllers) end up being refactored into not being singletons. Every. Single. One.

Thus, I've stopped writing singletons. I write classes that maintain state in instances, as any normal class should, and do so in isolation from other instances. If they are notification heavy, they always pass self as the notifying object. They have delegates. They maintain internal state. They avoid globals outside of truly global state.

And, often, there may be exactly one instance of said class in my app(s). That one instance acts like a singleton and, in fact, I might even create a convenient way of getting a hold of it, likely through the App's delegate or through a class method (that might even be named sharedInstance sometimes).

Said class includes tear-down code that is typically divided in two pieces; code to persist current state for restoration later and code that releases instance associated resources.

Convenient like a singleton. Ready to be multiply instantiated when needed.

bbum
  • 162,346
  • 23
  • 271
  • 359
3

Sure no problem. You provide a new class method: [MyClass killSingleton]; That method releases the singleton and sets its internal reference to nil. The next time someone asks [MyClass sharedSingleton], you go through the same steps you did before to create it.

EDIT: actually, in the old days such a routine could have overridden the release selector, and refused to go away. So as the first comment below states, this is an object with static scope - its kept alive through a static variable keeping a retain count of 1 on the object. However, by adding in a new class method to nil out that ivar (under ARC), thereby releasing it, achieves the desired result. Control over instantiating and releasing the static object is completely done via class methods, so is easy to maintain and debug.

David H
  • 40,852
  • 12
  • 92
  • 138
  • 1
    This is no longer a singleton then >< just a regular object that has static scope. – borrrden Aug 14 '12 at 02:26
  • 1
    Your comment is more appropriate for the question than the answer. I'm just answering questions, not judging people on their use of technical terms. I'm sure if we were discussing this at a bar, with a pint in hand, we'd all come to a different solution to whatever the problem is. – David H Aug 14 '12 at 10:44
1

It's against the concept of Singleton, but it can be implemented the following way for an ARC based project

//ARC
@interface Singleton : NSObject

+ (Singleton *)sharedInstance;
+ (void)selfDestruct;

@end

@implementation Singleton

static Singleton *sharedInstance = nil;

+ (Singleton *)sharedInstance {
    if (sharedInstance == nil) {
        sharedInstance = [[Singleton alloc] init];
    }
    return sharedInstance;
}

+ (void) selfDestruct {
    sharedInstance = nil;
}

@end
ram
  • 921
  • 7
  • 8
  • 1
    This won't work. If you checked the sharedInstance after setting it to nil, it will exist in memory. Also, this doesn't provide any help if the sharedManager is retained in any other object. – Abdalrahman Shatou Mar 08 '14 at 00:51
  • When ARC is around, setting sharedInstance to nil will release it (unless someone else holds yet another reference to it). but this code is alright, and will release the single object. However - the sharedInstance implementation is not thread-safe, and can fail on several concurrency situations, leading to either 2 different instances in memory (one of them lost and leak) or none at all while you think you do have one... – Motti Shneor Feb 02 '22 at 10:39
0
//This can be implemented using bool variable. If bool no create new instance.  

@interface Singleton : NSObject

+ (Singleton *)sharedInstance;

@end

@implementation Singleton

static Singleton *sharedInstance = nil;

+ (Singleton *)sharedInstance {

        if (!keepInstance) {
               sharedInstance = [[Singleton alloc] init];
               keepInstance = YES;
        }
        return sharedInstance;
}

@end
  • This sketch isn't thread-safe on the singleton's side. use dispatch_once and once_token for the sharedInstance implementation. – Motti Shneor Feb 02 '22 at 10:36
0

I needed to clear out the singleton so I ended up doing the following:

- (void)deleteSingleton{
@synchronized(self) {
    if (sharedConfigSingletone != nil) {
        sharedConfigSingletone = nil;
    }
}
}

Hope it helps.

Gal Blank
  • 2,070
  • 2
  • 18
  • 18