내 Objective-C 싱글톤은 어떤 모습이어야 할까요?
싱글톤 액세스 방식은 보통 다음 중 하나입니다.
static MyClass *gInstance = NULL;
+ (MyClass *)instance
{
@synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
이를 개선하기 위해 무엇을 할 수 있을까요?
하나의 은 '보다 낫다'를 입니다.+(void)initialize
매뉴얼에서 다음 항목을 참조하십시오.
시 " " 가 송신됩니다.
initialize
(따라서 클래스를 사용하지 않으면 메서드가 호출되지 않습니다). 시 " " 가 송신됩니다.initialize
스레드 세이프 방식으로 클래스에 메시지를 보냅니다.슈퍼클래스는 서브클래스보다 먼저 이 메시지를 수신합니다.
따라서 다음과 같은 작업을 수행할 수 있습니다.
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
@interface MySingleton : NSObject
{
}
+ (MySingleton *)sharedSingleton;
@end
@implementation MySingleton
+ (MySingleton *)sharedSingleton
{
static MySingleton *sharedSingleton;
@synchronized(self)
{
if (!sharedSingleton)
sharedSingleton = [[MySingleton alloc] init];
return sharedSingleton;
}
}
@end
아래의 다른 답변에 따르면, 다음과 같이 해야 한다고 생각합니다.
+ (id)sharedFoo
{
static dispatch_once_t once;
static MyFoo *sharedFoo;
dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; });
return sharedFoo;
}
Kendall이 잠금 비용을 회피하는 스레드 세이프 싱글톤을 올렸기 때문에 저도 하나 던져야겠다고 생각했습니다.
#import <libkern/OSAtomic.h>
static void * volatile sharedInstance = nil;
+ (className *) sharedInstance {
while (!sharedInstance) {
className *temp = [[self alloc] init];
if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
[temp release];
}
}
return sharedInstance;
}
좋아, 어떻게 작동하는지 설명해줄게
실행 중: 일반 실행 중
sharedInstance
되어 있기 에, 「 」는 「 」로 설정되어 있습니다.while
루프는 실행되지 않으며 단순히 변수의 존재를 테스트한 후 함수가 반환됩니다.면 if : If 면면
sharedInstance
않는CAS')을하여 할당 및됩니다.CAS'는 Compare And Swap('CAS')이다.2개의 의 콜을 시행하는 경우
sharedInstance
동시에sharedInstance
동시에 존재하지 않는 경우, 양쪽 모두 싱글톤의 새로운 인스턴스를 초기화하고 CAS를 제 위치에 배치하려고 합니다.이 CAS가 , 되면, 설정된) CAS가 됩니다.sharedInstance
★★★★★★★★★★★★★★★.OSAtomicCompareAndSwapPtrBarrier
는 설정 스레드의 쓰기 장벽과 테스트 스레드의 읽기 장벽으로 기능합니다.
정적 MyClass *sharedInst = 0; + (id)shared Instance{@synchronize(self) {if ( sharedInst == nill )의 경우,/* sharedInst는 init */로 셋업됩니다.[[self allocate]init]; }}sharedInst를 반환한다.} - (id)init{if ( sharedInst != nill ) {[NSException 상승:NSInternalInconsistencyException(NSInternalInconsistencyEx형식:@"[%@%@]을(를) 호출할 수 없습니다.대신 +[%@%@]를 사용합니다." ,NSString From Class([셀프 클래스]), NSString From Selector(_cmd),NSString From Class([셀프 클래스]),NSString From Selector(@selector(shared Instance))"; } else if ( self = [ super init ]) {sharedInst = 자체; /* 여기에 특정 클래스가 있는 경우}sharedInst를 반환한다.} /* 이것들은 아마GC 앱싱글톤 유지실제로 싱글톤으로서비 CG 앱*/- (NSUIInterger)retain Count{NSIntergerMax를 반환한다.} - (단방향 보이드) 해제{} - (id) 표시{sharedInst를 반환한다.} - (id)자동 리스{sharedInst를 반환한다.}
편집: 이 실장은 ARC에 의해 폐지되었습니다.올바른 구현을 위해 ARC와 호환되는 Objective-C 싱글톤을 구현하는 방법을 참조하십시오.
다른 답변에서 읽은 초기화 구현은 모두 공통 오류를 공유합니다.
+ (void) initialize {
_instance = [[MySingletonClass alloc] init] // <----- Wrong!
}
+ (void) initialize {
if (self == [MySingletonClass class]){ // <----- Correct!
_instance = [[MySingletonClass alloc] init]
}
}
Apple 문서에서는 초기화 블록의 클래스 유형을 확인할 것을 권장합니다.기본적으로는 서브클래스가 초기화를 호출하기 때문입니다.KVO를 통해 간접적으로 서브클래스를 작성할 수 있는 명백한 경우가 있습니다.다른 클래스에 다음 행을 추가하는 경우:
[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
하여 Objective-C의 두 번째 를 MySingletonClass로 .+initialize
.
다음과 같이 init 블록에 중복 초기화가 없는지 암묵적으로 체크해야 합니다.
- (id) init { <----- Wrong!
if (_instance != nil) {
// Some hack
}
else {
// Do stuff
}
return self;
}
하지만 당신은 자신의 발을 쏘거나, 더 나쁜 것은 다른 개발자에게 자신의 발을 쏘게 할 기회를 줄 것이다.
- (id) init { <----- Correct!
NSAssert(_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self){
// Do stuff
}
return self;
}
TL;DR, 여기 구현이 있습니다.
@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
if (self == [MySingletonClass class]){
_instance = [[MySingletonClass alloc] init];
}
}
- (id) init {
ZAssert (_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self) {
// Initialization
}
return self;
}
+ (id) getInstance {
return _instance;
}
@end
(ZAssert를 자체 어설션 매크로로 대체하거나 NSAssert만으로 대체하십시오.
싱글톤 매크로 코드에 대한 자세한 설명은 코코아 위드 러브 블로그에 있습니다.
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html 를 참조해 주세요.
스레드는 안전하지만 초기화 후 잠기지 않는 흥미로운 shared Instance가 있습니다.요청하신 대로 상위 답변을 수정할 수 있을지는 아직 확실하지 않지만, 자세한 논의를 위해 제안합니다.
// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;
// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
return (MySingleton *)sharedInstance;
}
+ (MySingleton*)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [[MySingleton alloc] init];
// Replace expensive thread-safe method
// with the simpler one that just returns the allocated instance.
SEL origSel = @selector(sharedInstance);
SEL newSel = @selector(simpleSharedInstance);
Method origMethod = class_getClassMethod(self, origSel);
Method newMethod = class_getClassMethod(self, newSel);
method_exchangeImplementations(origMethod, newMethod);
}
}
return (MySingleton *)sharedInstance;
}
간단한 답변: 훌륭합니다.
장황한 답변: 예를 들면...
static SomeSingleton *instance = NULL;
@implementation SomeSingleton
+ (id) instance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (instance == NULL){
instance = [[super allocWithZone:NULL] init];
}
});
return instance;
}
+ (id) allocWithZone:(NSZone *)paramZone {
return [[self instance] retain];
}
- (id) copyWithZone:(NSZone *)paramZone {
return self;
}
- (id) autorelease {
return self;
}
- (NSUInteger) retainCount {
return NSUIntegerMax;
}
- (id) retain {
return self;
}
@end
dispatch/once.h 헤더를 읽고 무슨 일이 일어나고 있는지 확인하십시오.이 경우 헤더 코멘트는 docs 페이지 또는 man 페이지보다 더 적합합니다.
다른 클래스가 싱글톤 속성을 상속할 수 있도록 싱글톤을 클래스로 만들었습니다.
Singleton.h:
static id sharedInstance = nil;
#define DEFINE_SHARED_INSTANCE + (id) sharedInstance { return [self sharedInstance:&sharedInstance]; } \
+ (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }
@interface Singleton : NSObject {
}
+ (id) sharedInstance;
+ (id) sharedInstance:(id*)inst;
+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;
@end
Singleton.m:
#import "Singleton.h"
@implementation Singleton
+ (id) sharedInstance {
return [self sharedInstance:&sharedInstance];
}
+ (id) sharedInstance:(id*)inst {
@synchronized(self)
{
if (*inst == nil)
*inst = [[self alloc] init];
}
return *inst;
}
+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst {
@synchronized(self) {
if (*inst == nil) {
*inst = [super allocWithZone:zone];
return *inst; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
@end
여기 독신자가 되고 싶은 계급의 예가 있습니다.
#import "Singleton.h"
@interface SomeClass : Singleton {
}
@end
@implementation SomeClass
DEFINE_SHARED_INSTANCE;
@end
싱글톤 클래스의 유일한 제한은 NSObject 서브클래스라는 점입니다.그러나 대부분의 경우 싱글톤을 코드에 사용하고 있기 때문에 실제로는 NSObject 서브클래스이기 때문에 이 클래스는 매우 편리하고 코드를 깔끔하게 합니다.
이것은, 쓰레기가 없는 환경에서도 동작합니다.
@interface MySingleton : NSObject {
}
+(MySingleton *)sharedManager;
@end
@implementation MySingleton
static MySingleton *sharedMySingleton = nil;
+(MySingleton*)sharedManager {
@synchronized(self) {
if (sharedMySingleton == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedMySingleton;
}
+(id)allocWithZone:(NSZone *)zone {
@synchronized(self) {
if (sharedMySingleton == nil) {
sharedMySingleton = [super allocWithZone:zone];
return sharedMySingleton; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
-(void)dealloc {
[super dealloc];
}
-(id)copyWithZone:(NSZone *)zone {
return self;
}
-(id)retain {
return self;
}
-(unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be release
}
-(void)release {
//do nothing
}
-(id)autorelease {
return self;
}
-(id)init {
self = [super init];
sharedMySingleton = self;
//initialize here
return self;
}
@end
첫 번째 통화 후 잠금장치 고가의 사용을 피하고 스레드 세이프해야 하지 않을까요?
+ (MySingleton*)sharedInstance
{
if (sharedInstance == nil) {
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[MySingleton alloc] init];
}
}
}
return (MySingleton *)sharedInstance;
}
http://github.com/cjhanson/Objective-C-Optimized-Singleton
Matt Gallagher의 작업을 기반으로 하지만 Google의 Dave MacLachlan이 기술한 바와 같이 메서드 스위즐링을 사용하도록 구현을 변경합니다.
코멘트/기고를 환영합니다.
어때.
static MyClass *gInstance = NULL;
+ (MyClass *)instance
{
if (gInstance == NULL) {
@synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
}
return(gInstance);
}
그러면 초기화 후 동기화 비용이 발생하지 않게 되는 건가요?
Objective-C의 싱글톤 패턴에 대한 자세한 내용은 여기를 참조하십시오.
KLSingleton:
- 서브클래시블(n번째까지)
- ARC 대응
alloc
★★★★★★★★★★★★★★★★★」init
- 로딩이 느리다
- 스레드 세이프
- 잠금 없음(@synchronize가 아닌 +초기화 사용)
- 매크로 프리
- 스위즐 프리
- 간단하죠.
자기와 동기화하기 싫으면...아직 자기 물체는 존재하지 않기 때문에!임시 ID 값으로 잠기게 됩니다.클래스 메서드(sharedInstance, allocate, allocateWithZone: 등)를 다른 사용자가 실행할 수 없도록 해야 하므로 클래스 개체로 동기화해야 합니다.
@implementation MYSingleton
static MYSingleton * sharedInstance = nil;
+( id )sharedInstance {
@synchronized( [ MYSingleton class ] ) {
if( sharedInstance == nil )
sharedInstance = [ [ MYSingleton alloc ] init ];
}
return sharedInstance;
}
+( id )allocWithZone:( NSZone * )zone {
@synchronized( [ MYSingleton class ] ) {
if( sharedInstance == nil )
sharedInstance = [ super allocWithZone:zone ];
}
return sharedInstance;
}
-( id )init {
@synchronized( [ MYSingleton class ] ) {
self = [ super init ];
if( self != nil ) {
// Insert initialization code here
}
return self;
}
}
@end
잃어버리지 않게 그냥 여기에 놔두고 싶었어요이 제품의 장점은 Interface Builder에서 사용할 수 있다는 것입니다. 이는 엄청난 이점입니다.이것은 제가 질문한 다른 질문에서 가져온 것입니다.
static Server *instance;
+ (Server *)instance { return instance; }
+ (id)hiddenAlloc
{
return [super alloc];
}
+ (id)alloc
{
return [[self instance] retain];
}
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
instance = [[Server hiddenAlloc] init];
}
}
- (id) init
{
if (instance)
return self;
self = [super init];
if (self != nil) {
// whatever
}
return self;
}
static mySingleton *obj=nil;
@implementation mySingleton
-(id) init {
if(obj != nil){
[self release];
return obj;
} else if(self = [super init]) {
obj = self;
}
return obj;
}
+(mySingleton*) getSharedInstance {
@synchronized(self){
if(obj == nil) {
obj = [[mySingleton alloc] init];
}
}
return obj;
}
- (id)retain {
return self;
}
- (id)copy {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
if(obj != self){
[super release];
}
//do nothing
}
- (id)autorelease {
return self;
}
-(void) dealloc {
[super dealloc];
}
@end
이 "질문"에 대한 언급이 많은 것은 알지만, 싱글톤을 정의하기 위해 매크로를 사용하는 것을 제안하는 사람은 많지 않습니다.이것은 매우 일반적인 패턴이며 매크로가 싱글톤을 매우 단순화합니다.
다음은 제가 본 몇 가지 Objc 구현을 기반으로 작성한 매크로입니다.
Singeton.h
/**
@abstract Helps define the interface of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the implementation.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/
#define SingletonInterface(TYPE, NAME) \
+ (TYPE *)NAME;
/**
@abstract Helps define the implementation of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the interface.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/
#define SingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+ (void)initialize \
{ \
static BOOL initialized = NO; \
if(!initialized) \
{ \
initialized = YES; \
__ ## NAME = [[TYPE alloc] init]; \
} \
} \
\
\
+ (TYPE *)NAME \
{ \
return __ ## NAME; \
}
사용 예:
MyManager.h
@interface MyManager
SingletonInterface(MyManager, sharedManager);
// ...
@end
MyManager.m
@implementation MyManager
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
SingletonImplementation(MyManager, sharedManager);
// ...
@end
거의 비어 있는데 인터페이스 매크로가 왜 필요합니까?헤더와 코드 파일 간의 코드 일관성: 자동 메서드를 추가하거나 변경할 경우 유지 보수성.
여기서 가장 인기 있는 답변(작성 시)에 사용되는 싱글톤을 작성하기 위해 초기화 방법을 사용하고 있습니다.
Objective C 클래스 메서드에서는 다음과 같은 방법으로 싱글톤 패턴을 사용하지 않도록 할 수 있습니다.
[[Librarian sharedInstance] openLibrary]
대상:
[Librarian openLibrary]
Class Methods만 있는 다른 클래스 내에서 클래스를 래핑하면 인스턴스를 만들지 않기 때문에 실수로 중복 인스턴스를 만들 가능성이 없습니다.
자세한 블로그는 이쪽에서 작성했습니다.
예를 @robbie-hanson에서 확장하려면...
static MySingleton* sharedSingleton = nil;
+ (void)initialize {
static BOOL initialized = NO;
if (!initialized) {
initialized = YES;
sharedSingleton = [[self alloc] init];
}
}
- (id)init {
self = [super init];
if (self) {
// Member initialization here.
}
return self;
}
내 방법은 다음과 같이 간단하다.
static id instanceOfXXX = nil;
+ (id) sharedXXX
{
static volatile BOOL initialized = NO;
if (!initialized)
{
@synchronized([XXX class])
{
if (!initialized)
{
instanceOfXXX = [[XXX alloc] init];
initialized = YES;
}
}
}
return instanceOfXXX;
}
싱글톤이 이미 초기화되어 있는 경우 LOCK 블록은 입력되지 않습니다.두 번째 체크는 if(!initialized)가 현재 스레드가 LOCK을 취득했을 때 아직 초기화되지 않았는지 확인하는 것입니다.
모든 해결책을 다 읽은 것은 아니기 때문에 이 코드가 중복되어도 양해 바랍니다.
내 생각에 이것이 가장 안전한 구현이다.
+(SingletonObject *) sharedManager
{
static SingletonObject * sharedResourcesObj = nil;
@synchronized(self)
{
if (!sharedResourcesObj)
{
sharedResourcesObj = [[SingletonObject alloc] init];
}
}
return sharedResourcesObj;
}
나는 보통 Ben Hoffstein의 대답과 거의 비슷한 코드를 사용한다(Wikipedia에서도 그것을 얻었다).나는 Chris Hanson이 그의 코멘트에서 말한 이유로 그것을 사용한다.
단, 싱글톤을 NIB에 넣을 필요가 있는 경우가 있습니다.이 경우 다음을 사용합니다.
@implementation Singleton
static Singleton *singleton = nil;
- (id)init {
static BOOL initialized = NO;
if (!initialized) {
self = [super init];
singleton = self;
initialized = YES;
}
return self;
}
+ (id)allocWithZone:(NSZone*)zone {
@synchronized (self) {
if (!singleton)
singleton = [super allocWithZone:zone];
}
return singleton;
}
+ (Singleton*)sharedSingleton {
if (!singleton)
[[Singleton alloc] init];
return singleton;
}
@end
을 보류합니다.-retain
가비지 수집 환경에서는 위의 코드만 있으면 됩니다.
받아들여진 답변은 컴파일되지만 틀렸다.
+ (MySingleton*)sharedInstance
{
@synchronized(self) <-------- self does not exist at class scope
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
Apple 문서별:
... self 대신 Class 개체를 사용하여 연관된 클래스의 클래스 메서드를 동기화할 수 있습니다.
셀프워크를 사용해도 안 되고 복사 붙여넣기 실수처럼 보입니다.클래스 팩토리 방식의 올바른 실장은 다음과 같습니다.
+ (MySingleton*)getInstance
{
@synchronized([MySingleton class])
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
언급URL : https://stackoverflow.com/questions/145154/what-should-my-objective-c-singleton-look-like
'sourcecode' 카테고리의 다른 글
WPF: 단순한 TextBox 데이터 바인딩 (0) | 2023.04.19 |
---|---|
예기치 않은 bash 종료에서 생성된 임시 파일을 제거하는 중 (0) | 2023.04.19 |
WPF에서 GridViewColumn 데이터를 자동 조정하고 오른쪽 정렬하려면 어떻게 해야 합니까? (0) | 2023.04.19 |
텍스트 블록을 WPF ListBox로 강제 랩하다 (0) | 2023.04.14 |
간접 확장이란 무엇입니까?${!var*}의 의미는 무엇입니까? (0) | 2023.04.14 |