Wingzero Wingzero - 5 months ago 32
Objective-C Question

Xcode 7.3 reports compiler error if use NS_UNAVAILABLE in its implementation

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.

Answer

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