1

So I am writing a singleton object and want to mark the init method as NS_UNAVAILABLE or

__attribute__((unavailable("Use 'sharedInstance' instead of 'init' as this class is singleton."))); from Safe way to create singleton with init method in Objective-C second answer.

The question is for the second answer, it does not work:

However, Xcode 7.3 prompts me a compiler error in the singleton implementation:

@interface NetWorkService : NSObject

+(nonnull instancetype)sharedInstance;
-(nonnull instancetype)init NS_UNAVAILABLE;

@end

@implementation NetWorkService

+(instancetype)sharedInstance {
    static NetWorkService *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[NetWorkService alloc] init]; <--- init is unavailable
    });
    return sharedInstance;
}

@end

Is this a bug or what I missed? Thanks.

Community
  • 1
  • 1
Wingzero
  • 9,644
  • 10
  • 39
  • 80
  • 2
    You told clang that it's unavailable and then you're surprised when it tells you it's unavailable? What am I missing? – Avi Jun 30 '16 at 06:23
  • 1
    @Avi I am just tried to create the shared instance and not allow users of my code to use init. It' from http://stackoverflow.com/questions/29748175/safe-way-to-create-singleton-with-init-method-in-objective-c, second answer – Wingzero Jun 30 '16 at 06:24
  • The accepted answer on that question is the same I was going to give. Why don't you use it? – Avi Jun 30 '16 at 06:27
  • @Avi I know the accepted answer works, but I am trying the new ones. – Wingzero Jun 30 '16 at 06:28
  • This is totally different question for http://stackoverflow.com/questions/29748175/safe-way-to-create-singleton-with-init-method-in-objective-c, this question is asking why the second answer from the post does not work – Wingzero Jun 30 '16 at 06:44

1 Answers1

0

You can create a private initializer and call [super init] from it, I think Clang should not complain about availability of -init at that point.

- (instancetype)init {
    @throw @"Use -sharedInstance.";
    return nil;
}

- (instancetype)privateInit {
   return [super init];
}

+ (instancetype)sharedInstance {
    static id sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] privateInit];
    });
    return sharedInstance;
}

and don't tell anyone that private initializer exists and hope that nobody finds out.

Surely there are other ways you can ensure that only one instance of class exists.

Such as to have some sort of Proxy and return a shared instance of private hidden class that conforms to certain protocol. This way you hide object instantiation behind some sort of factory that you can create but not being able to do anything with it anyway.

@protocol PublicInterface <NSObject>

@required
- (void)doSomething;

@end

@interface PrivateObject : NSObject<PublicInterface> @end
@implementation PrivateObject

- (void)doSomething {}

@end

@interface Proxy : NSObject

+ (id<PublicInterface>)sharedInstance;

- (instancetype)init NS_UNAVAIALBLE;

@end

@implementation Proxy

- (instancetype)init {
    @throw @"Use -sharedInstance instead.";
    return nil;
}

+ (id<PublicInterface>)sharedInstance {
    static id sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[PrivateObject alloc] init];
    });
    return sharedInstance;
}

@end
pronebird
  • 12,068
  • 5
  • 54
  • 82
  • thanks for the answer, however, my true question is about why the second answer from the original post does not work – Wingzero Jun 30 '16 at 10:35
  • @Wingzero you've got an answer for that in comments below your post. If you mark method unavailable, obviously, you cannot call it on the same class, however you can call super implementation without restrictions. – pronebird Jun 30 '16 at 10:41
  • nope, the second answer said: `In this case init function will not be available from outside, will not be suggested for autocompletion, and you'll be able to normally use the init method inside implementation file.` I am asking why it does not work, are you saying this answer is wrong? – Wingzero Jun 30 '16 at 10:43
  • @Wingzero there is no exception for implementation files. First line of your implementation file imports header that says -init is unavailable. period. :) The answer you referred to is wrong. – pronebird Jun 30 '16 at 10:47
  • @Wingzero you can try creating extension such as `@interface NetWorkService () - (instancetype)init; @end` within your implementation file to see if that somehow would discard the effect of availability attribute from header file. – pronebird Jun 30 '16 at 10:51