Pergunta

According to Apple Docs NSCalendar is not thread-safe. How can I ensure thread-safety when I use the static method currentCalendar ?

Any library could call the same method. How can I lock the access?

Foi útil?

Solução

You can use NSLock.

NSLock *lock = [[NSLock alloc] init];

and

[lock lock];
//calendar
[lock unlock];

Outras dicas

I would expect that creating a Calendar would be thread safe (each time -currentCalendar is called you get a new instance), simultaneously mutating an instance would not.

You may want to use a proxy to wrap all methods in a synchronized block. See my class BMProxy below (with threadSafe set to YES). I use this class to reuse NSDateFormatters and NSCalendars across multiple threads and this works like a charm. You can use it for instances of other non-thread-safe classes as well.

Usage is for example as follows:

+ (NSCalendar *)threadSafeCalendar {
    static NSCalendar *calender = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSCalendar *c = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        [c setTimeZone:[NSTimeZone timeZoneWithName:@"CET"]];
        calender = (NSCalendar *)[BMProxy proxyWithObject:c threadSafe:YES retained:YES];
    });
    return calender;
}

The BMProxy class:

#import <Foundation/Foundation.h>

/**
 Proxy that delegates all messages to the specified object
 */
@interface BMProxy : NSProxy {
@private
    __weak NSObject *_object;
    __strong NSObject *_retainedObject;
    BOOL _threadSafe;
}

/**
 The target object of the proxy.
 */
@property(readonly, weak) NSObject *object;

/**
 Whether the proxy should be thread-safe (make all methods synchronized) or not.
 */
@property(atomic, assign) BOOL threadSafe;

/**
 Initializer with the designated target object.

 Defaults to threadSafe = NO and retained = YES.

 @param object The proxied object
 */
- (id)initWithObject:(NSObject *)object;

/**
 Initializer with the designated target object and whether the proxy should be thread-safe or not.

 Defaults to retained = YES.

 @param object The proxied object
 @param threadSafe Whether the proxy should synchronize all methods or not.
 */
- (id)initWithObject:(NSObject *)object threadSafe:(BOOL)threadSafe;

/**
 Designated initializer. 

 The retained parameter determines whether the target object is retained or not.
 */
- (id)initWithObject:(NSObject *)object threadSafe:(BOOL)threadSafe retained:(BOOL)retained;

+ (BMProxy *)proxyWithObject:(NSObject *)object threadSafe:(BOOL)threadSafe retained:(BOOL)retained;

@end

@implementation BMProxy 

@synthesize threadSafe = _threadSafe;
@synthesize object = _object;

+ (BMProxy *)proxyWithObject:(NSObject *)object threadSafe:(BOOL)threadSafe retained:(BOOL)retained {
    return [[BMProxy alloc] initWithObject:object threadSafe:threadSafe retained:retained];
}

- (id)initWithObject:(NSObject *)theObject {
    return [self initWithObject:theObject threadSafe:NO retained:YES];
}

- (id)initWithObject:(NSObject *)theObject threadSafe:(BOOL)b {
    return [self initWithObject:theObject threadSafe:b retained:YES];
}

- (id)initWithObject:(NSObject *)theObject threadSafe:(BOOL)b retained:(BOOL)retained {
    _object = theObject;
    if (retained) {
        _retainedObject = theObject;
    }
    self.threadSafe = b;
    return self;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [_object methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if (self.threadSafe) {
        @synchronized(_object) {
            [anInvocation setTarget:_object];
            [anInvocation invoke];
        }
    } else {
        [anInvocation setTarget:_object];
        [anInvocation invoke];
    }
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    BOOL responds = [super respondsToSelector:aSelector];
    if (!responds) {
        responds = [_object respondsToSelector:aSelector];
    }
    return responds;
}

@end
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top