0

I have the following code:

{
    NSObject *_object;
}

- (instancetype)init {
    if (self = [super init]) {
        _object = [[NSObject alloc] init];
    }

    return self;
}

- (NSObject*)object {
    return _object;
}

If the method object is called from a second thread after init has completed and returned, how do I know the assignment to _object within init will be visible and it's not actually returning an unassigned pointer?

What is the internal mechanism that guarantees this?

Monstieur
  • 7,992
  • 10
  • 51
  • 77

2 Answers2

3

The thread-safety of your code depends on how it is used, and how it is intended to be used is inherently thread-safe. You shouldn't be passing around partially constructed objects, therefore the allocation and initialization ([[... alloc] init] or new) should be confined to a single thread and then passed around to other threads.

Joe
  • 56,979
  • 9
  • 128
  • 135
  • I don't care about the contents of the array. That's a separate issue. My question is about the visibility of the `_array` assignment in `init`. If I `init` in thread A and call the method in thread B, is the method guaranteed to see `nil` for `_array`. – Monstieur Mar 18 '15 at 13:16
  • I'm not referring to the contents, I'm referring to the initial creation of the array in your `init`. What I am getting at there is no need for a locking mechanism in your `init` method. – Joe Mar 18 '15 at 13:17
  • How is it partially constructed? `[NSMutableArray array]` creates and returns an empty array. This is the object I want to retrieve. – Monstieur Mar 18 '15 at 13:18
  • I mean even after `init` has completed and returned, how does it guarantee that the assignment to `_array` is immediately visible to all other threads unless there is a memory barrier after `init`? – Monstieur Mar 18 '15 at 13:20
  • It is not partially constructed, the point I was getting at is the code that uses your class should be calling `alloc/init` or `new` to fully construct the object, at which point your code is thread-safe due to thread confinement. – Joe Mar 18 '15 at 13:20
  • So there is automatically a memory barrier after `init` that guarantees the `init` code is instantly visible to all other threads? Even if the method is called on different CPU core from the one than executed `init`? – Monstieur Mar 18 '15 at 13:22
  • Yes, that is correct unless you plan on using your class as a singleton. The singleton variable would need a memory barrier and now there is an even easier way using [dispatch_once](http://stackoverflow.com/questions/7568935/how-do-i-implement-an-objective-c-singleton-that-is-compatible-with-arc) – Joe Mar 18 '15 at 13:25
  • What thread confinement exactly? If I call `init` on thread A, wait for it to return, pass the object to thread B and call another method from thread B (thread B is a message loop that's already running - it was started before thread A), how is thread B's method call guaranteed to see the assignment's in thread A's `init`? It could be sitting in a different CPU's core's cache. Doesn't it have to be forced to write and read from memory with `volatile`? – Monstieur Mar 18 '15 at 13:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73252/discussion-between-joe-and-locutus). – Joe Mar 18 '15 at 13:42
1

Use dispatch_once. This is guaranteed to run only one time no matter how many threads there are. For example

+ (MyClass *)sharedInstance
{
    //  Static local predicate must be initialized to 0
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}
ahwulf
  • 2,584
  • 15
  • 29