gasapladev
gasapladev

Reputation: 697

ios stop 2 threads from using a method at the same time

We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.

We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.

When I do this, updatePresence Method call, my program works perfectly

ViewController.m

-(void)loadConversationScreen{
    [conversationController updatePresence];
}

But when I do this, something goes wrong with my program

ViewController.m

-(void)loadConversationScreen{
    [conversationController performSelectorInBackground:@selector(updatePresence) withObject:nil];
    [conversationController updatePresence];

}

This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.

How do I PROPERLY stop 2 threads from using a method at the same time?

How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?

My app should support ios 4.0 and up

Advance thanks to all for your help.

Upvotes: 3

Views: 2551

Answers (2)

Steven Fisher
Steven Fisher

Reputation: 44886

The best thread lock for you is @sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.

Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want @synchronized(self) {}:

-(void)updateVariables {
    @synchronized(self) {
        _foo = 1;
        _bar = 2;
    }
}

@sycnhronized is re-entrant in the sense that the same thread can call @sycnhronized as deeply as it wants, for instance:

- (void)a {
    @synchronized(self) {
        // entered "immediately" if called from b, where the @synchronized has
        // already been called
        _foo = _foo + 1;
    }
}

- (void)b {
    @synchronized(self) {
        [self a];
    }
}

For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:

- (void)someTask {
    dispatch_async( dispatch_get_main_queue(), ^{
        [self updateUI];
    });
}

- (void)updateUI {
    NSAssert( [NSThread isMainThread], @"called from non-main thread" );
    // do UI updates here
}

Upvotes: 6

Rui Peres
Rui Peres

Reputation: 25927

As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.

Upvotes: 2

Related Questions