我知道这有几个主题,但没有人回答我的问题。
我已经像这样实现了我的单身人士课程(意识到关于单身人士的争议):
var code = 'The Code';
var text = 'The Text';
var ip = 'The IP';
document.write('<table><tr><th>Name</th><td><a href="javascript:alert(\''+code+'\')">' + text + '</a></td></tr><tr><th>IP Address</th><td>' + ip + '</td></tr><tr><th>Code</th><td><a href="javascript:alert(\'empty string\');">' + code + '</a></td></tr></table>');
我尝试实例化一个不同的对象,并将它与sharedInstance返回的对象与'=='进行比较,它们确实不同。
问题:
答案 0 :(得分:14)
您的观察是正确的,您在Objective-C中看到的许多“单例”模式根本不是单例,而是可以创建其他实例的“共享实例”模型。
在旧的MRC时代,Apple曾经有过示例代码,展示了如何实现真正的单身人士。
您拥有的代码是ARC和线程安全单例的推荐模式,您只需将其放在init
方法中:
- (instancetype) init
{
static MyClass *initedObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
initedObject = [super init];
});
return initedObject;
}
此代码将确保只有MyClass
的实例,无论进行了多少次[MyClass new]
或[[MyClass alloc] init]
次调用。
这就是你需要所做的一切,但你可以走得更远。首先,如果您希望有一个类方法来返回单例,那么它只是:
+ (instancetype) singletonInstance
{
return [self new];
}
此方法最终调用init
,返回单例,并在需要时创建它。
如果MyClass
实施NSCopying
,那么您还需要实施copyWithZone:
- 这是copy
调用的方法。你有一个单身人士,这很简单:
- (instancetype) copyWithZone:(NSZone *)zone
{
return self;
}
最后,在Objective-C中,分配新对象实例并对其进行初始化的操作是不同的。上述方案确保只初始化和使用MyClass
的一个实例,但是对于new
或alloc
的每次调用,都会分配另一个实例,然后由init
立即丢弃并清除通过ARC。这有点浪费!
通过实施allocWithZone:
(与上面的copy
类似,alloc
实际上最终调用的方法)遵循与init
相同的模式,可以轻松解决这个问题:
+ (instancetype) allocWithZone:(NSZone *)zone
{
static MyClass *allocatedObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
allocatedObject = [super allocWithZone:zone];
});
return allocatedObject;
}
第一次创建实例时,allocWithZone:
将分配它,然后init
将初始化它,所有后续调用将返回已存在的对象。没有丢弃不需要的分配。
就是这样,一个真正的单身人士,并不比那些常见的人造单身人士更难。
HTH
答案 1 :(得分:5)
您不能将init
方法设为私有,就像使用构造函数在Java中一样。因此,没有什么可以阻止你调用[[MyClass alloc] init]
来确实创建一个不同的对象。只要你不这样做,但坚持使用sharedInstance
方法,你的实现就可以了。
你可以做什么:让init
方法引发异常(例如使用[self doesNotRecognizeSelector:@_cmd]
)并以不同的方法(例如privateInit
)执行初始化头文件。
答案 2 :(得分:3)
使用objective-c,可以防止单例类创建多个对象。您可以使用单例类来阻止alloc和init调用。
#import <Foundation/Foundation.h>
@interface SingletonClass : NSObject
+ (id) sharedInstance;
- (void) someMethodCall;
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
@end
#import "SingletonClass.h"
@implementation SingletonClass
+ (id) sharedInstance{
static SingletonClass * sharedObject = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObject = [[self alloc] initPrivate];
});
return sharedObject;
}
- (instancetype)init {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"You can't override the init call in class %@", NSStringFromClass([self class])] userInfo:nil];
}
- (instancetype)initPrivate {
if (self = [super init]) {
}
return self;
}
- (void) someMethodCall{
NSLog(@"Method Call");
}
@end
1#如果您尝试在SingletonClass上调用init或new方法,则无法调用这些方法。
2#如果您在头文件中注释掉下面提到的方法并尝试在SingletonClass方法上调用init,那么应用程序将崩溃,原因是“您无法覆盖类SingletonClass中的init调用”。
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
只需使用此代码为Singleton Pattern创建单个对象,并防止对来自其他类的singleton模式的alloc init调用。我用xCode 7.0+测试了这段代码并且工作正常。
答案 3 :(得分:2)
要防止创建单个类的多个对象,您需要执行以下操作。你可以很好地创建单例对象。但是在调用init,copy,mutable copy时,你需要处理这种方式。
- (instancetype)init{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
- (id)copy{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
同样的东西也是可变副本。所以这个实现确保一旦一个实例可用..
愿这对你有所帮助。
答案 4 :(得分:1)
@VeryPoliteNerd只需在init
上将new
和.h
方法标记为不可用:
- (instancetype)init __attribute__((unavailable("Use +[MyClass sharedInstance] instead")));
+ (instancetype)new __attribute__((unavailable("Use +[MyClass sharedInstance] instead")));
如果调用者尝试手动实例化此对象,这将导致编译器抱怨
答案 5 :(得分:0)
调用alloc / init来获取Singleton类的第二个实例被认为是一个明显的编程错误。为了避免这种编程错误,你不要编写复杂的代码来防止它,你做代码审查并告诉每个人都试图这样做,就像你遇到任何编程错误一样。
答案 6 :(得分:0)
这对我有用:
static AudioRecordingGraph * __strong sharedInstance;
+(instancetype)sharedInstance {
@synchronized(self) {
if(!sharedInstance) {
sharedInstance = [AudioRecordingGraph new];
}
return sharedInstance;
}
}
答案 7 :(得分:0)
我想为现代的Objective-C单例实现最佳Swift互操作性添加观察。但是,让我从代码开始。我们先假设该类是用于管理一系列请求的,所以我可以称其为RequestManager
:
// RequestManager.h
NS_ASSUME_NONNULL_BEGIN
@interface RequestManager : NSObject
@property (nonatomic, strong, readonly, class) RequestManager *sharedManager;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)copy NS_UNAVAILABLE;
- (instancetype)mutableCopy NS_UNAVAILABLE;
- (void)someMethod;
@end
NS_ASSUME_NONNULL_END
和
// RequestManager.m
@implementation RequestManager
+ (instancetype)sharedManager {
static RequestManager *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
//setup code
}
return self;
}
- (void)someMethod { ... }
@end
注意:
注意,我将单例设为类属性而不是类方法。
从Objective-C方面来说,这是没有区别的区别,但是从Swift来说,您可以做MyClass.shared
之类的事情而不会受到()
的干扰。这是苹果公司所有单身人士都采用的模式。
一个人可能会给单身人士一个更有意义的名称。例如,如果类是RequestManager
,则单例可能称为sharedManager
,而不是sharedInstance
。
如果执行此操作,Swift将自动检测实例名称的显着部分,并将其以shared
而非sharedInstance
或其他任何形式显示给Swift。
如果您确实要在Objective-C代码中使用名称sharedInstance
,则只需提供NS_SWIFT_NAME
的显式shared
即可采用美观,简洁和Swift中一致的单例命名惯例:
@property (nonatomic, strong, readonly, class) RequestManager *sharedInstance NS_SWIFT_NAME(shared);
请注意NS_UNAVAILABLE
的使用。这样可以防止呼叫者意外实例化自己的副本。例如。来自Objective-C:
或者从Swift:
也许不用说,我已经审核了此方法的可空性,即使用NS_ASSUME_NONNULL_BEGIN
/ END
来将默认值设置为非空。或者,显然,您可以将单例属性标记为nonnull
。
答案 8 :(得分:-2)
这对我有用:
static DataModel *singleInstance;
+ (DataModel*)getInstance{
if (singleInstance == nil) {
singleInstance = [[super alloc] init];
}
return singleInstance;
}
您可以使用
进行调用_model = [DataModel getInstance];