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

4 Answers 4

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].

Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly the sort of knowledgeable answer I was looking for. Thanks.
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.

2 Comments

Is it safe to have the final "_myString = aString;" outside of the dispatch_once block?
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.
0
- (NSString *)doSomething:(NSUInteger)aParam { 

    static NSString *foo = nil;

    if (!foo) {
        //load foo
    }

    return @"";  
}

Comments

0

This will guide you properly.

Singleton Instance

A note on Objective-C singletons

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.