初次使用案例
如果有人沒有碰到類似問題,可以嘗試看看。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSString *urlString =@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg";
NSString *encodeURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:encodeURLString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
});
代碼很簡答,就是使用NSURLConnection下載一個dmg格式的大文件。不一樣的是這次我們還順帶使用到了GCD。
若看官們還不了解GCD,簡書好多大神都寫過相關(guān)介紹GCD的文章。
下面繼續(xù)貼出關(guān)于NSURLConnection的delegate方法。
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
}
好了,萬事具備,讓我們愉快地在delegate方法中下一個斷點來運行。
很快我們就會發(fā)現(xiàn)這根本不會調(diào)用代理方法。
開始思考
其實思考的方向也很明確,既然是線程系的鈴,我們不妨用其來解。
我也就不賣關(guān)子,原因其實就是:線程在delegate方法回調(diào)之前就已經(jīng)提前結(jié)束了。
如果這原因讓你傻眼了,別急,我來用一個簡單的例子來說明。
- (void)viewDidLoad {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(methodOne) object:nil];
[thread start];
[self performSelector:@selector(methodTwo) onThread:thread withObject:nil waitUntilDone:NO];
NSLog(@"開始");
}
- (void)methodOne {
NSLog(@"HI~");
}
- (void)methodTwo {
NSLog(@"Hello~");
}
Run!!!告訴你結(jié)果:
2015-10-19 17:44:36.469 Collection[5597:373711] HI~
2015-10-19 17:44:36.469 Collection[5597:373446] 開始
像這類問題就是因為導(dǎo)致了線程在執(zhí)行結(jié)束后銷毀。因此沒法快樂地說Hello~
而這其中還涉及到一個東西:RunLoop。
這里也不過多介紹,稍微簡單地科普一下:

- 主線程默然是啟動RunLoop的。
- 子線程剛創(chuàng)建的時候沒有RunLoop,需要自己手動去添加。
- 主線程會在app結(jié)束后銷毀RunLoop。子線程結(jié)束后銷毀RunLoop。
說到這里也順帶說一下,我們廣泛使用的AFNetWorking,這個第三方網(wǎng)絡(luò)請求框架會開啟一個新線程來添加自己runloop事件。
無論使用NSOperation+NSURLConnection并發(fā)模型或者&GCD的并發(fā)模型,NSURLConnection遇到的這種無法回調(diào)的問題。
AFNetWorking是這樣解決的,單獨建立起一個global thread,內(nèi)置runLoop,所有的connection都由這個runloop發(fā)起,回調(diào)也是它接收,不占用主線程,也不耗CPU資源。
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread =
[[NSThread alloc] initWithTarget:self
selector:@selector(networkRequestThreadEntryPoint:)
object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
動手解決
既然不想讓線程提前結(jié)束,通常我們會用以下方式來解決。
- 創(chuàng)建NSTimer,掛載事件源,強行不讓線程提前結(jié)束,當(dāng)然這種方法太Low,我選擇無視。
- 向創(chuàng)建的RunLoop添加NSPort,讓線程不會自己停下,然后添加判斷,來推出循環(huán)。
不多說,上代碼。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSString *urlString =@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg";
NSString *encodeURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:encodeURLString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if (self.connection) {
NSPort* port = [NSPort port];
NSRunLoop* rl = [NSRunLoop currentRunLoop]; // Get the runloop
[rl addPort:port forMode:NSDefaultRunLoopMode];
[self.connection scheduleInRunLoop:rl forMode:NSDefaultRunLoopMode];
}
while(!_isFinished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
});
由于本文介紹到了線程和RunLoop這兩個開發(fā)大家樂于討論的概念
大家就自行查閱啦~~