NSURLConnection-Blocking-Wrapper mit Semaphoren implementiert [geschlossen]

Bei meinem letzten Projekt bin ich auf die Notwendigkeit gestoßen:

Daten blockierend herunterladen (wird in einem Hintergrund-Thread gestartet)Sie können die Daten aber auch schrittweise verarbeiten, während sie empfangen werden (da die heruntergeladenen Daten leicht 100 MB groß sein könnten, war es nicht effizient, alle Daten in einer großen NSD-Datei zu speichern *).

Ich musste daher ein asynchrones NSURLConnection-Objekt verwenden (um die Daten progressiv empfangen zu können), es jedoch in einen Container einschließen, der den aufrufenden Thread "zwischen" zwei aufeinanderfolgenden blockierteconnection:didReceiveData: Anrufe delegieren, und bis entwederconnectionDidFinishLoading: oderconnection:didFailWithError: wurden genannt.

Ich dachte, ich würde meine Lösung teilen, da ich einige Stunden brauchte, um die richtigen Codeteile zusammenzustellen, die hier und da zu finden sind (in StackOverflow und anderen Foren).

Der Code startet grundsätzlich einen neuenNSURLConnection auf einem Hintergrund-Thread (dispatch_get_global_queue), legt die Ausführungsschleife fest, um die Delegatenanrufe empfangen zu können, und verwendetdispatch_semaphores um die aufrufenden und die Hintergrund-Threads "abwechselnd" zu blockieren. Dasdispatch_semaphores Code ist schön in einem Brauch verpacktProducerConsumerLock Klasse.

BlockingConnection.m

#import "BlockingConnection.h"
#import "ProducerConsumerLock.h"

@interface BlockingConnection()

@property (nonatomic, strong) ProducerConsumerLock* lock;

@end

@implementation BlockingConnection

- (id)initWithURL:(NSURL*) url callback:(void(^)(NSData* data)) callback {
    if (self = [super init]) {
        self.lock = [ProducerConsumerLock new];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
            [NSURLConnection connectionWithRequest:request delegate:self];
            while(!self.lock.finished) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
        });

        [self.lock consume:^(NSData* data) {
            if (callback != nil) {
                callback(data);
            }
        }];
    }
    return self;
}

+ (void) connectionWithURL:(NSURL*) url callback:(void(^)(NSData* data)) callback {
    BlockingConnection* connection;
    connection = [[BlockingConnection alloc] initWithURL:url callback:callback];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.lock produce:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.lock produce:nil];
    [self.lock finish];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.lock finish];
}

@end

ProducerConsumerLock.h

@interface ProducerConsumerLock : NSObject

@property (atomic, readonly) BOOL finished;

- (void) consume:(void(^)(id object)) block;
- (void) produce:(id) object;
- (void) finish;

@end

ProducerConsumerLock.m

#import "ProducerConsumerLock.h"

@interface ProducerConsumerLock() {
    dispatch_semaphore_t consumerSemaphore;
    dispatch_semaphore_t producerSemaphore;
    NSObject* _object;
}

@end

@implementation ProducerConsumerLock

- (id)init {
    if (self = [super init]) {
        consumerSemaphore = dispatch_semaphore_create(0);
        producerSemaphore = dispatch_semaphore_create(0);
        _finished = NO;
    }
    return self;
}

- (void) consume:(void(^)(id)) block {
    BOOL finished = NO;
    while (!finished) {
        dispatch_semaphore_wait(consumerSemaphore, DISPATCH_TIME_FOREVER);
        finished = _finished;
        if (!finished) {
            block(_object);
            dispatch_semaphore_signal(producerSemaphore);
        }
    }
}

- (void) produce:(id) object {
    _object = object;
    _finished = NO;
    dispatch_semaphore_signal(consumerSemaphore);
    dispatch_semaphore_wait(producerSemaphore, DISPATCH_TIME_FOREVER);
}

- (void) finish {
    _finished = YES;
    dispatch_semaphore_signal(consumerSemaphore);
}

- (void)dealloc {
    dispatch_release(consumerSemaphore);
    dispatch_release(producerSemaphore);
}

@end

Die BlockingConnection-Klasse kann aus dem Hauptthread (dies würde jedoch den Hauptthread blockieren) oder aus einer benutzerdefinierten Warteschlange verwendet werden:

dispatch_async(queue, ^{
    [BlockingConnection connectionWithURL:url callback:^(NSData *data) {
        if (data != nil) {
            //process the chunk of data as you wish
            NSLog(@"received %u bytes", data.length);
        } else {
            //an error occurred
        }
    }];
    NSLog(@"finished downloading");
});

Wenn Sie Kommentare oder Vorschläge haben, seien Sie bitte willkommen!

Antworten auf die Frage(0)

Ihre Antwort auf die Frage