Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
364 views
in Technique[技术] by (71.8m points)

Singleton in iOS Objective C doesn't prevent more than one instance

I know there are several threads on this, but none answer my questions.

I've implemented my singleton class like this (being aware of the controversy about singletons):

+ (MyClass*) sharedInstance {
    static MyClass *_sharedInstance = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _sharedInstance = [[MyClass alloc] init];
    });
    return _sharedInstance;
}

- (instancetype)init{
    self = [super init];
    if (self) {
        //setup code
    }
    return self;
}

I tried instantiating a different object and compared it to the one returned by sharedInstance with '==' and they were indeed different.

Questions:

  1. Shouldn't creating more than one object of the singleton class be impossible? Isn't that the point? Singleton implementation in Java prevents it.
  2. And if so, how? Should I make a setup method and call it instead of having the init implemented and doing it?
  3. Is this correct implementation?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your observation is correct, many of the "singleton" patterns you see in Objective-C are not singletons at all but rather a "shared instance" model where other instances can be created.

In the old MRC days Apple used to have sample code showing how to implement a true singleton.

The code you have is the recommended pattern for ARC and thread-safe singletons, you just need to place it in the init method:

- (instancetype) init
{
   static MyClass *initedObject;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
      initedObject = [super init];
   });
   return initedObject;
}

This code will ensure that there is only ever one instance of MyClass regardless of how many [MyClass new] or [[MyClass alloc] init] calls are made.

That is all you need to do, but you can go further. First if you wish to have a class method to return the singleton it is simply:

+ (instancetype) singletonInstance
{
   return [self new];
}

This method ends up calling init which returns the singleton, creating it if needed.

If MyClass implements NSCopying then you also need to implement copyWithZone: - which is the method which copy calls. As you've a singleton this is really simple:

- (instancetype) copyWithZone:(NSZone *)zone
{
   return self;
}

Finally in Objective-C the operations of allocating a new object instance and initialising it are distinct. The above scheme ensures only one instance of MyClass is initialised and used, however for every call to new or alloc another instance is allocated and then promptly discarded by init and cleaned up by ARC. This is somewhat wasteful!

This is easily addressed by implementing allocWithZone: (like copy above this is the method alloc actually ends up calling) following the same pattern as for init:

+ (instancetype) allocWithZone:(NSZone *)zone
{
   static MyClass *allocatedObject;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
      allocatedObject = [super allocWithZone:zone];
   });
   return allocatedObject;
}

The first time an instance is created then allocWithZone: will allocate it and then init will initialise it, all subsequent calls will return the already existing object. No discarded unneeded allocations.

That's it, a true singleton, and no harder than the faux-singletons that are so common.

HTH


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...