代表和performSelectorOnMainThread

时间:2011-04-18 11:30:31

标签: iphone multithreading uiview delegates nsnotifications

我对这两者的使用感到有些困惑。

我有一个后台线程,负责下载数据并将其应用到iOS设备中的核心数据数据库。

后台线程中的代码调用共享实例类ProgressController来更新UI上的进度(我知道在主线程中运行)。然后,ProgressController有一个委托,由顶层的View Controller分配。

它的工作正常,但后台线程启动时UI不会更新。我知道正在调用委托,因为我已经使用正在传递的文本触发了NSLog。

现在我读到我应该使用performSelectorOnMainThread,但鉴于委托正在解雇,这似乎是多余的。

我应该使用performSelectorOnMainThread而不是使用委托。

我错过了什么吗?

如果有人能解释,我会非常感激。

谢谢,

克里斯。

在后台主题中

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

...

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

在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

在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

在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;

}

3 个答案:

答案 0 :(得分:1)

委托方法本身与线程没有特定的关系。它只是一个向另一个发送消息的对象。如果恰好在后台线程上调用了该委托方法,那么任何UI交互仍然必须在主线程上完成。

有关详细信息,请参阅文档:http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html%23//apple_ref/doc/uid/TP40002974-CH7-SW18

某些对象将委托调用与特定线程相关联。例如,NSURLConnection的+ connectionWithRequest:delegate:,其文档说明:

  

发送给委托的消息将在调用此方法的线程上发送。为了使连接正常工作,调用线程的运行循环必须在默认的运行循环模式下运行。

因此,简而言之,是的,使用委托方法和performSelectorOnMainThread来更新UI非常有可能。这样做没有错。

答案 1 :(得分:1)

您插入的代码显示您直接从后台线程调用委托方法。要在主线程(您应该使用)上执行GUI工作,您需要在调用委托方法时或在委托方法本身中直接使用performSelectorOnMainThread:。如果要在这些更新期间停止后台线程,可以使用waitUntilDone:YES的变体。

答案 2 :(得分:-1)

您应该使用NSURLConnection类异步下载数据,然后您可以使用其委托方法来更新您的UI

参考此代码 -

    // create the URL
    NSURL *postURL = [NSURL URLWithString:@"http://twitpic.com/api/uploadAndPost"];

    // create the connection
    NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:postURL
                                                               cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                           timeoutInterval:30.0];

    // change type to POST (default is GET)
    [postRequest setHTTPMethod:@"POST"];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:postRequest delegate:self];

    if( theConnection )
    {
        webData = [[NSMutableData data] retain];
    }

委托方法 -

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// update your UI here.. 
    [webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"ERROR with theConenction");
    [connection release];
    [webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//  your data downloaded completely.. do you code here.. 

}