Nickolas
Nickolas

Reputation: 790

How to use local static object in Objective-C?

How to use local static objects in Objective-C?

- (void)foo {
    static NSMutableArray *mutableArr = nil;
    // initialize mutableArr somehow somewhere 
    // using mutableArr several times
}

Upvotes: 1

Views: 2314

Answers (3)

ipmcc
ipmcc

Reputation: 29886

If your goal is simply to have a singleton, it seems a common, accepted pattern for that these days is as follows:

#import <dispatch/dispatch.h>

+ (NSMyObjectType*)sharedMyObject
{
    static dispatch_once_t pred;
    static NSMyObjectType* sValue;
    dispatch_once(&pred, ^{ sValue = [[NSMyObjectType alloc] init]; } );
    return sValue;
}

This will be safe for concurrent access from multiple threads, with respect to leaking instances of NSMyObjectType, assuming NSMyObjectType is immutable. Also note that unless you have a specific reason to do otherwise (for instance, adopting a specific @protocol), it makes sense to make such accessors class methods instead of instance methods. This gives the API consumer a hint that this is a shared resource (as does using the word shared in the method name.)

If you are vending a mutable shared data structure for concurrent access from multiple threads, you'll want to use a type that is inherently safe for such use, or you'll want to provide that safety yourself as the API vendor. For instance, if you wanted to vend an NSMutableSet, you might do something like this:

@class NSFoo;

@interface SharedFooVender : NSObject

+ (NSSet*)sharedFoos;
+ (void)addSharedFoo:(NSFoo*)foo;
+ (void)removeSharedFoo:(NSFoo*)foo;

@end

@implementation SharedFooVender

static NSMutableSet* pPrivateSharedMutableSetOfFoos()
{
    static NSMutableSet* sSetOfFoos = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sSetOfFoos = [[NSMutableSet alloc] init];
    });
    return sSetOfFoos;
}

+ (NSSet*)sharedFoos
{
    NSMutableSet* sharedFoos = pPrivateSharedMutableSetOfFoos();
    @synchronized(sharedFoos)
    {
        return [[sharedFoos copy] autorelease];
    }    
}

+ (void)addSharedFoo:(NSFoo*)foo
{
    if (nil == foo) return;
    NSMutableSet* sharedFoos = pPrivateSharedMutableSetOfFoos();
    @synchronized(sharedFoos)
    {
        [sharedFoos addObject: foo];
    }
}

+ (void)removeSharedFoo:(NSFoo*)foo
{
    if (nil == foo) return;
    NSMutableSet* sharedFoos = pPrivateSharedMutableSetOfFoos();
    @synchronized(sharedFoos)
    {
        [sharedFoos removeObject: foo];
    }
}

@end

By synchronizing access to and mutation of the actual underlying mutable object, and vending the whole Set as an immutable copy, you provide safety for concurrent access from multiple threads (although it should be noted that by making immutable copies, consumers could hang on to stale copies, and in essence, every immutable copy should be assumed to be stale from the instant it's created.)

FWIW, there are a million different approaches to this -- I'm not claiming that this approach is perfect for every (or even any scenario). But relying on consumers of your API to provide locking for you is a recipe for disaster.

Now, all that said, the accepted answer is fine, assuming that you only allow single threaded access. If you only expect this to be used from the main thread (perhaps in a UIKit context), you can use the accepted answer, although I might suggest asserting [NSThread isMainThread] so that you fail early if someone later starts calling your shared accessor from background threads.

Upvotes: 5

Reuven
Reuven

Reputation: 2132

I hope I'm not missing anything, but this is how I'm using it:

- (void)foo {
    static NSMutableArray *mutableArr = nil;
    if (mutableArr == nil) {
        mutableArr = [[NSMutableArray alloc] init...];
        // add more first time initialization as required
    }
    assert(mutableArr);
    // now, use mutableArr freely...
}

Upvotes: 0

nyanev
nyanev

Reputation: 11519

if use static var. It's good way to check if it is initialized before init the var.

Upvotes: 0

Related Questions