2

I'm writing a multithreaded application for iPhone, and I'm using NSLock's to make sure that some operations (such as loading sounds from file) will behave as atomic. To simplify acquiring locks from different parts of my application, I wrote following class, that lets me lock and unlock locks just by passing NSString with name. If such lock doesn't exist, it creates it and saves for future. I tested it and it seems to work fine (as it just provides access to NSLock objects and doesn't alter their behavior).

My question is: Is it ok to have and use such class, or do I have some misunderstanding in concept of locks?

Locker.h

#import <Foundation/Foundation.h>

@interface Locker : NSObject { }

+ (void) purgeLocks;
+ (NSLock*) lockWithName: (NSString*) name;
+ (void) lock: (NSString*) name;
+ (void) unlock: (NSString*) name;
+ (BOOL) tryLock: (NSString*) name;

@end



Locker.m

#import "Locker.h"

static NSMutableDictionary* locks = nil;

@implementation Locker

+ (void) initialize {
    locks = [[NSMutableDictionary dictionary] retain];
}

+ (void) purgeLocks {
    [locks release];
    [Locker initialize];
}

+ (NSLock*) lockWithName: (NSString*) name {
    NSLock* lock = nil;
    @synchronized([Locker class]) {
        lock = [locks objectForKey: name];
        if(!lock) {
            lock = [[[NSLock alloc] init] autorelease];
            [lock setName: name];
            [locks setObject: lock forKey: name];
        }
    }
    return lock;
}

+ (void) lock: (NSString*) name {
    [[Locker lockWithName: name] lock];
}

+ (void) unlock: (NSString*) name {
    [[Locker lockWithName: name] unlock];
}

+ (BOOL) tryLock: (NSString*) name {
    return [[Locker lockWithName: name] tryLock];
}

@end
Least
  • 23
  • 3

2 Answers2

1

@synchronized is slow, avoid it. Allocate a lock in + initialize and use that instead. But I'd go for a singleton instead of using class methods, but that's more or less a matter of taste.

The major drawback to your approach is that you have to lock a lock in order to get another lock. In a heavily multihreaded app this meta-lock (your current @synchronized) can become a performance bottleneck even if the threads all want to access different locks.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
0

This looks quite heavy to me. Are you sure you can’t simplify the design? (GCD comes to mind.) Almost every iOS application is a multithreaded one, and IMHO it’s rare to see such locking helpers as you have written. If there’s a place where the KISS principle shines, I’m sure it’s multithreading.

zoul
  • 102,279
  • 44
  • 260
  • 354
  • Thanks, I will look into GCD. Above is just what came to my mind first. The main problem I'm dealing with, is when user presses play button, I start loading sound from file in another thread, but if user quickly presses stop button, this message goes useless, as sound is still loading. When if finally loads, it starts to play, and this is already not needed. I tried to start all related methods in same thread, but there was some problems I ran into. KISS is great, only sometimes it's hard to know, is what you doing simple or not :) For some reason, this code looks simple to me. – Least Sep 15 '11 at 07:37
  • I don’t know the details, but dispatch queues sound like a good match for what you describe. (You’d just dispatch all control messages onto one serial queue. That will make sure the stop event won’t come before the audio is loaded.) For me, the telling signs of departing from the KISS principle would be class methods, helper “manager” objects and multiple locks. YMMV. – zoul Sep 15 '11 at 07:50