4

In an Objective-C class, I want to load just once the contents of a text file into an NSString so that it can be used by all instances of that class.

In the Java world, I learnt over the years that it is easy to get this subtly wrong in terms of thread safety if you don't use a proven idiom. So I'd like to make sure I use the correct idiom.

Can you show me an example of an Objective-C class that does this?

Here's my empty class that I'm starting with...

@interface TestClass : NSObject
- (NSString *)doSomething:(NSUInteger)aParam;
@end

@implementation TestClass {
}

- (id)init {
    self = [super init];
    if (self) {

    }
    return self;
}

- (NSString *)doSomething:(NSUInteger)aParam {
     // something with shared NSString loaded from text file, 
     //  depending on the value of aParam
     return @""; 
}
@end
Steve McLeod
  • 51,737
  • 47
  • 128
  • 184

4 Answers4

7

An idiomatic way of dealing with static properties in Objective C is hiding them behind class methods (the ones with +). Declare your string as a static inside the implementation of your class method, and use dispatch_once for initialization:

+ (id)stringFromFile {
    static dispatch_once_t once;
    static NSString *sharedString;
    dispatch_once(&once, ^{
        sharedString = [NSString
            stringWithContentsOfFile:@"MyFile"
            encoding:... // ...supply proper parameters here...
            error:...];
    });
    return sharedString;
}

This way of setting up static objects is thread-safe. The sharedString will be initialized once, even if the method is called concurrently from several threads.

Now you can get to your string from anywhere by calling [MyClass stringFromFile].

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

Create an instance variable for your class instances to access, and a static variable inside your designated initialiser. Your designated initialiser should create the string object once (keeping it in the static variable) and assign it to the instance variable every time. For instance:

@implementation TestClass {
    NSString *_myString;
}

- (id)init {
    self = [super init];
    if (self == nil) return nil;

    static dispatch_once_t once;
    static NSString *aString = nil;
    dispatch_once(&once, ^{
        aString = ... // However you want to instantiate the string
    });
    _myString = aString;

    return self;
}

This lets you access the string in your instance methods as if it were a normal instance variable, despite the fact the string is created only once and each instance is pointing to the single object.

Jim
  • 72,985
  • 14
  • 101
  • 108
  • Is it safe to have the final "_myString = aString;" outside of the dispatch_once block? – Steve McLeod Aug 28 '12 at 14:15
  • 1
    Yes. It would be incorrect if you put it inside the `dispatch_once` block - the whole point of that block is that it only ever executes once. You need that assignment to execute for every instance of your class, otherwise only one instance of your class will have a non-nil `_myString`. `aString` will only ever have the one value, and the assignment will only ever happen after that value is set. – Jim Aug 28 '12 at 14:41
0
- (NSString *)doSomething:(NSUInteger)aParam { 

    static NSString *foo = nil;

    if (!foo) {
        //load foo
    }

    return @"";  
}
Tom Fobear
  • 6,729
  • 7
  • 42
  • 74
0

This will guide you properly.

Singleton Instance

A note on Objective-C singletons

Community
  • 1
  • 1
alloc_iNit
  • 5,173
  • 2
  • 26
  • 54
  • But is what I'm describing a Singleton? I'll have many instances of my class, but I want all of them to share access to one specific NSString, initialised when the first instance of the class is created. – Steve McLeod Aug 28 '12 at 13:33