Iphone 用信号量实现的NSURLConnection阻塞包装器

Iphone 用信号量实现的NSURLConnection阻塞包装器,iphone,objective-c,ios,macos,nsurlconnection,Iphone,Objective C,Ios,Macos,Nsurlconnection,在我最近的项目中,我偶然发现需要: 以阻塞方式下载数据(在后台线程中启动) 但也要在接收数据时逐步处理数据(因为下载的数据可能很容易达到100米,所以将其全部存储在一个大数据中并不高效*) 因此,我需要使用一个异步NSURLConnection对象(以便能够逐步接收数据),但将其包装在一个容器中,该容器将阻止调用线程“在”两个连续的connection:didReceiveData:delegate调用之间,直到调用了connectiondifinishload:或connection:di

在我最近的项目中,我偶然发现需要:

  • 以阻塞方式下载数据(在后台线程中启动)
  • 但也要在接收数据时逐步处理数据(因为下载的数据可能很容易达到100米,所以将其全部存储在一个大数据中并不高效*)
因此,我需要使用一个异步NSURLConnection对象(以便能够逐步接收数据),但将其包装在一个容器中,该容器将阻止调用线程“在”两个连续的
connection:didReceiveData:
delegate调用之间,直到调用了
connectiondifinishload:
connection:didfailwitheror:

我想我会与大家分享我的解决方案,因为我花了好几个小时来整理这里和那里(在StackOverflow和其他论坛上)找到的正确代码片段

代码基本上在后台线程上启动一个新的
NSURLConnection
dispatch\u get\u global\u queue
),将运行循环设置为能够接收代理调用,并使用
dispatch\u信号量
以“交替”的方式阻止调用和后台线程。
dispatch\u信号量
代码被很好地包装在一个定制的
ProducerConsumerLock
类中

阻塞连接.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
#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
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 "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
#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
BlockingConnection类可以从主线程(但这会阻止主线程)或从自定义队列使用:

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");
});
如果您有任何意见或建议,欢迎

遵循SO的QA格式