Search code examples
iphonemultithreadinguiviewdelegatesnsnotifications

Delegates and performSelectorOnMainThread


I'm slightly confused over the use of these two.

I have a background thread which does the heavy lifting of downloading data and applying it to the Core Data Database within the iOS device.

The code within the background thread calls a shared instance class ProgressController to update progress on the UI (which I know runs in the main thread). ProgressController then has a delegate which is assigned by the View Controller on top.

Its all working fine, except that the UI does not update once the background thread has started. I know the delegate is being called because I have NSLogs firing with the text that's being passed through.

Now I read that I should be using performSelectorOnMainThread, but that seems superfluous given that the delegate is firing.

Should I use performSelectorOnMainThread instead and not use delegates at all.

Am I missing something?

If someone could explain, I'd be really grateful.

Thanks,

Chris.

Within Background Thread

progressController = [ProgressController sharedInstance];
[progressController open];

....

[progressController updateProgress:NSLocalizedString(@"Update text here", @"Update text here")];

Within ProgressController.h

#import <Foundation/Foundation.h>

@protocol ProgressControllerDelegate 
@required
- (void) displayProgress:(NSString *)text;
- (void) showProgress;
- (void) hideProgress;

@end

@interface  ProgressController : NSObject {

    NSString    *currentProgress;
    BOOL        progressOnDisplay;
    id          delegate;
}

+ (ProgressController *)sharedInstance;

@property (nonatomic) BOOL  progressOnDisplay;
@property (nonatomic, assign) id delegate;

-(void) open;
-(void) updateProgress:(NSString *)text;
-(void) reDisplayProgress;
-(void) close;

@end

Within ProgressController.m #import "ProgressController.h"

@implementation ProgressController

@synthesize progressOnDisplay;
@synthesize delegate;

static ProgressController *sharedInstance;

+ (ProgressController *)sharedInstance {
    @synchronized(self) {
        if (!sharedInstance)
        [[ProgressController alloc] init];              
    }
    return sharedInstance;
}

+(id)alloc {
    @synchronized(self) {
        NSAssert(sharedInstance == nil, NSLocalizedString(@"Attempted to allocate a second instance of a singleton ProgressController.", @"Attempted to allocate a second instance of a singleton ProgressController."));
        sharedInstance = [super alloc];
    }
    return sharedInstance;
}
-(id) init {
    if (self = [super init]) {
        [self open];
    }
    return self;
}

// Ask delegate to show new Progress Label
-(void) open {
    progressOnDisplay = TRUE;
    currentProgress = @"";
    [self.delegate showProgress];
}

// Ask delegate to update and display Progress text
-(void) updateProgress:(NSString *)text {
    currentProgress = text;
    [self.delegate displayProgress:currentProgress];

}

// Ask delegate display existing Progress text if any
-(void) reDisplayProgress {
    if (currentProgress != @"") {
        [self.delegate displayProgress:currentProgress];
        [self.delegate showProgress];   
    }
}

// Ask delegate to clear and hide Progress Label
-(void) close {
    progressOnDisplay = FALSE;
    currentProgress = @"";
    [self.delegate hideProgress];
}


@end

Within View Controller

- (void)viewDidLoad {
    [super viewDidLoad];

    progressController = [ProgressController sharedInstance];
    progressController.delegate = self;
    [progressController reDisplayProgress]; // In case progress has been updated prior to the view load

}

// Delegate method to show Progress Label
- (void) showProgress {
    progressView.hidden = FALSE;    

}

// Delegate method to display specific text in Progress label
- (void) displayProgress:(NSString *)text {
    [progressLabel setText:text];
    [progressView setNeedsDisplay];

    DLog(@"Reporting -  %s", [text UTF8String]);  // I can see that this is firing successfully

}

// Delegate method to hide Progress Label
- (void) hideProgress {
    progressView.hidden = TRUE;

}

Solution

  • Your inserted code shows that you call the delegate method directly from the background thread. To perform the GUI work on the main thread (which you are supposed to), you need to use performSelectorOnMainThread: either directly when calling the delegate's methods or in the delegate methods themselves. If you want to stop your background thread during these updates, you might use the variants with waitUntilDone:YES.