如何在objective-c中创建“并发队列安全”延迟加载器(单例管理器)

时间:2011-02-12 11:29:04

标签: iphone objective-c ios singleton thread-safety

我创建了这个类,将任何对象转换为单例,但我知道它不是“并发队列安全”。有人可以向我解释如何做到这一点,或者更好的是,向我展示代码。为了清楚起见,我想知道如何在 iOS 上使用操作队列和调度队列(NSOperationQueue和Grand Central Dispatch)。

提前致谢,

编辑:我知道如何做到这一点。如果有人可以为我确认,我会这样做并发布代码。这个想法是代理队员自己完成队列。因此,如果我为它应该返回的任何对象创建一个可变代理(就像Apple在键值编码/观察中所做的那样),并且总是为相同的对象/标识符对返回相同的代理(使用相同类型的延迟加载技术)因为我曾经创建单例),代理会自动将任何消息排队到单例,并使其完全是线程安全的。

恕我直言,这似乎要做很多工作,所以如果它不起作用我不想这样做,或者它会让我的应用程序慢慢爬行。

这是我的非线程安全代码:

RMSingletonCollector.h

//
//  RMSingletonCollector.h
//  RMSingletonCollector
//
//  Created by Rich Meade-Miller on 2/11/11.
//  Copyright 2011 Rich Meade-Miller. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "RMWeakObjectRef.h"

struct RMInitializerData {
    // The method may take one argument.
    // required
    SEL designatedInitializer;
    // data to pass to the initializer or nil.
    id data;
};
typedef struct RMInitializerData RMInitializerData;

RMInitializerData RMInitializerDataMake(SEL initializer, id data);

@interface NSObject (SingletonCollector)

// Returns the selector and data to pass to it (if the selector takes an argument) for use when initializing the singleton.
// If you override this DO NOT call super.
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier;

@end


@interface RMSingletonCollector : NSObject {

}

+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier;

+ (void)destroyCollection;
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier;

@end

// ==--==--==--==--==Notifications==--==--==--==--==

extern NSString *const willDestroySingletonCollection;
extern NSString *const willDestroySingletonCollectionObject;

RMSingletonCollector.m

//
//  RMSingletonCollector.m
//  RMSingletonCollector
//
//  Created by Rich Meade-Miller on 2/11/11.
//  Copyright 2011 Rich Meade-Miller. All rights reserved.
//

#import "RMSingletonCollector.h"
#import <objc/objc-runtime.h>

NSString *const willDestroySingletonCollection = @"willDestroySingletonCollection";
NSString *const willDestroySingletonCollectionObject = @"willDestroySingletonCollectionObject";

RMInitializerData RMInitializerDataMake(SEL initializer, id data) {
    RMInitializerData newData;
    newData.designatedInitializer = initializer;
    newData.data = data;
    return newData;
}

@implementation NSObject (SingletonCollector)

+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier {
    return RMInitializerDataMake(@selector(init), nil);
}

@end


@interface RMSingletonCollector ()

+ (NSMutableDictionary *)singletonCollection;
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection;

@end


@implementation RMSingletonCollector

static NSMutableDictionary *singletonCollection = nil;

+ (NSMutableDictionary *)singletonCollection {
    if (singletonCollection != nil) {
        return singletonCollection;
    }
    NSMutableDictionary *collection = [[NSMutableDictionary alloc] initWithCapacity:1];
    [self setSingletonCollection:collection];
    [collection release];
    return singletonCollection;
}

+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection {
    if (newSingletonCollection != singletonCollection) {
        [singletonCollection release];
        singletonCollection = [newSingletonCollection retain];
    }
}

+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
    id obj;
    NSString *key;
    if (identifier) {
        key = [className stringByAppendingFormat:@".%@", identifier];
    }
    else {
        key = className;
    }

    if (obj = [[self singletonCollection] objectForKey:key]) {
        return obj;
    }
    // dynamic creation.
    // get a class for 
    Class classForName = NSClassFromString(className);
    if (classForName) {
        obj = objc_msgSend(classForName, @selector(alloc));
        // if the initializer takes an argument...
        RMInitializerData initializerData = [classForName designatedInitializerForIdentifier:identifier];
        if (initializerData.data) {
            // pass it.
            obj = objc_msgSend(obj, initializerData.designatedInitializer, initializerData.data);
        }
        else {
            obj = objc_msgSend(obj, initializerData.designatedInitializer);
        }
        [singletonCollection setObject:obj forKey:key];
            [obj release];
    }
    else {
        // raise an exception if there is no class for the specified name.
        NSException *exception = [NSException exceptionWithName:@"com.RMDev.RMSingletonCollector.failed_to_find_class" reason:[NSString stringWithFormat:@"SingletonCollector couldn't find class for name: %@", [className description]] userInfo:nil];
        [exception raise];
        [exception release];
    }
    return obj;
}

+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier {
    id obj = [self collectionObjectForType:className identifier:identifier];
    RMWeakObjectRef *objectRef = [[RMWeakObjectRef alloc] initWithObject:obj identifier:identifier];
    return [objectRef autorelease];
}

+ (void)destroyCollection {
    NSDictionary *userInfo = [singletonCollection copy];
    [[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollection object:self userInfo:userInfo];
    [userInfo release];
    // release the collection and set it to nil.
    [self setSingletonCollection:nil];
}
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
    NSString *key;
    if (identifier) {
        key = [className stringByAppendingFormat:@".%@", identifier];
    }
    else {
        key = className;
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollectionObject object:[singletonCollection objectForKey:key] userInfo:nil];
    [singletonCollection removeObjectForKey:key];
}

@end

RMWeakObjectRef.h

//
//  RMWeakObjectRef.h
//  RMSingletonCollector
//
//  Created by Rich Meade-Miller on 2/12/11.
//  Copyright 2011 Rich Meade-Miller. All rights reserved.
//

// In order to offset the performance loss from always having to search the dictionary, I made a retainable, weak object reference class.

#import <Foundation/Foundation.h>

@protocol RMWeakObjectReference <NSObject>

@property (nonatomic, assign, readonly) id objectRef;
@property (nonatomic, retain, readonly) NSString *className;
@property (nonatomic, retain, readonly) NSString *objectIdentifier;

@end


@interface RMWeakObjectRef : NSObject <RMWeakObjectReference>
{
    id objectRef;
    NSString *className;
    NSString *objectIdentifier;
}
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier;

- (void)objectWillBeDestroyed:(NSNotification *)notification;


@end

RMWeakObjectRef.m

//
//  RMWeakObjectRef.m
//  RMSingletonCollector
//
//  Created by Rich Meade-Miller on 2/12/11.
//  Copyright 2011 Rich Meade-Miller. All rights reserved.
//

#import "RMWeakObjectRef.h"
#import "RMSingletonCollector.h"

@implementation RMWeakObjectRef

@dynamic objectRef;
@synthesize className, objectIdentifier;

- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier {
    if (self = [super init]) {
        NSString *classNameForObject = NSStringFromClass([object class]);
        className = classNameForObject;
        objectIdentifier = identifier;
        objectRef = object;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectWillBeDestroyed:) name:willDestroySingletonCollectionObject object:object];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectWillBeDestroyed:) name:willDestroySingletonCollection object:[RMSingletonCollector class]];
    }
    return self;
}

- (id)objectRef {
    if (objectRef) {
        return objectRef;
    }
    objectRef = [RMSingletonCollector collectionObjectForType:className identifier:objectIdentifier];
    return objectRef;
}

- (void)objectWillBeDestroyed:(NSNotification *)notification {
    objectRef = nil;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [className release];
    [super dealloc];
}

@end

1 个答案:

答案 0 :(得分:0)

对singleton的最简单(和线程安全)访问:

static SomeClass* shared_instance;
+(SomeClass*) sharedInstance {
    @synchronized( shared_instance ) {
        if( !shared_instance ) {
            shared_instance = [[SomeClass alloc] init];
            //some additional initialization here
        }
    }

    return shared_instance;
}

编辑: 我认为特别的单身课程将是你最好的选择。即使是懒惰的装载。例如。您需要访问应用程序中的某些用户。然后你可以创建单例类:

的UserManager:

+(UserManager*) sharedManager;

-(NSArray*) allUsers;
-(NSArray*) recentUsers;
-(NSArray*) featuredUsers;

-(void) addUser:(User*) user;
-(void) removeUser:(User*) user;
etc...

然后,您将能够访问每个视图控制器中的数组。 您应该为其他类型创建单例。