Делегаты и выполняютSelectorOnMainThread - PullRequest
2 голосов
/ 18 апреля 2011

Я немного растерялся из-за использования этих двух.

У меня есть фоновый поток, который выполняет тяжелую загрузку данных и применяет их к Базовой базе данных на устройстве iOS.

Код в фоновом потоке вызывает класс общего экземпляра ProgressController для обновления прогресса в пользовательском интерфейсе (который, как я знаю, выполняется в основном потоке). Затем ProgressController имеет делегата, который назначается View Controller сверху.

Все работает нормально, за исключением того, что пользовательский интерфейс не обновляется после запуска фонового потока. Я знаю, что делегат вызывается, потому что я запускаю NSLogs с текстом, который передается.

Теперь я прочитал, что должен использовать executeSelectorOnMainThread, но это кажется излишним, учитывая, что делегат увольняется.

Должен ли я вместо этого использовать executeSelectorOnMainThread и вообще не использовать делегатов.

Я что-то упустил?

Если бы кто-то мог объяснить, я был бы очень благодарен.

Спасибо

Крис.

В фоновом потоке

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;

}

Ответы [ 3 ]

1 голос
/ 18 апреля 2011

Метод делегата сам по себе не имеет конкретной связи с потоком. Это просто один объект, отправляющий сообщение другому. Если этот метод делегата вызван в фоновом потоке, то любое взаимодействие с пользовательским интерфейсом все равно должно выполняться в основном потоке.

Для получения дополнительной информации см. Документы: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html%23//apple_ref/doc/uid/TP40002974-CH7-SW18

Некоторые объекты связывают вызовы делегатов с определенным потоком. Например, NSURLConnection's + connectionWithRequest:delegate:, документация которого гласит:

Сообщения делегату будут отправляться в потоке, который вызывает этот метод. Для правильной работы соединения цикл выполнения вызывающего потока должен работать в режиме цикла выполнения по умолчанию.

Итак, в общем, да, очень возможно использовать оба метода делегата и performSelectorOnMainThread для обновления вашего пользовательского интерфейса. В этом нет ничего плохого.

1 голос
/ 18 апреля 2011

Вставленный код показывает, что вы вызываете метод делегата непосредственно из фонового потока.Чтобы выполнить работу с графическим интерфейсом в главном потоке (который вы должны), вам нужно использовать performSelectorOnMainThread: либо непосредственно при вызове методов делегата, либо в самих методах делегата.Если вы хотите остановить фоновую ветку во время этих обновлений, вы можете использовать варианты с waitUntilDone:YES.

0 голосов
/ 18 апреля 2011

вы должны загрузить данные асинхронно, используя класс NSURLConnection, затем вы можете использовать его методы делегата для обновления вашего пользовательского интерфейса

см. Этот код -

    // 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.. 

}
...