【问题标题】:Delegates and performSelectorOnMainThread委托和 performSelectorOnMainThread
【发布时间】:2011-04-18 11:30:30
【问题描述】:

我对这两个的使用有点困惑。

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

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

一切正常,只是后台线程启动后 UI 不会更新。我知道代理正在被调用,因为我让 NSLogs 触发了正在传递的文本。

现在我读到我应该使用 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

在视图控制器内

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

}

【问题讨论】:

  • 请在后台线程中显示调用 ProgressController 方法的代码。

标签: iphone multithreading uiview delegates nsnotifications


【解决方案1】:

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

【讨论】:

  • 谢谢 Eiko。因此,例如在委托 (ProgressController.m) 中,应该 [self.delegate displayProgress:currentProgress];成为 [self.delegate :currentProgress] performSelectorOnMainThread:@selector(displayProgress:) withObject:currentProgress waitUntilDone:NO]]; ?
  • 它应该变成类似 [self.delegate performSelectorOnMainThread:@selector(displayProgress:) withObject:currentProgress]; (未经测试,只是在这里输入)
  • 你是明星 Eiko。谢谢!我在委托中需要的行格式为 [self.delegate performSelectorOnMainThread:@selector(displayProgress:) withObject:currentProgress waitUntilDone:NO];正如您所说,这意味着从主线程调用委托,并且 UI 现在更新。现在一切正常。
【解决方案2】:

委托方法本身与线程没有特定关系。它只是一个对象向另一个对象发送消息。如果该委托方法恰好在后台线程上调用,那么任何 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。这样做没有错。

【讨论】:

    【解决方案3】:

    你应该使用 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.. 
    
    }
    

    【讨论】:

      猜你喜欢
      • 2016-05-01
      • 1970-01-01
      • 2011-01-12
      • 1970-01-01
      • 2018-11-20
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多