1. NSURLConnection简介
(1)构造函数和调度函数
- (
nullable
instancetype
)initWithRequest:(NSURLRequest *)request delegate:(
nullable
id
)delegate startImmediately:(
BOOL
)startImmediately
该方法是NSURLConnection的默认构造函数,startImmediately参数,如果为YES,代表会将当前的connection实例加入到当前的runloop中,该connection的delegate的回调方法都会在当前线程执行,自动实现调度,所以这种情况下甚至是不需要调用 -start方法来开始请求;如果为NO,则需要手动调度,将当前的connection加入到一个线程的runloop中(如果不添加,默认会添加到当前线程的runloop中)。
- (
nullable
instancetype
)initWithRequest:(NSURLRequest *)request delegate:(
nullable
id
)delegate
+ (nullable NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate
上述的实例构造函数和类构造的本质是一样的,最终会调用到默认构造函数,startImmediately参数为YES。
NSURLConnection的执行必须是在一个开启runloop的线程中的,如果执行线程没有开启runloop,connection在start之后,可能会收不到回调方法。PS:主线程的runloop默认是开启的。
在某些开源的网络框架中,开始一个connection的时候,都会开启当前线程的runloop,比如AFNetworking中,所有的网络请求都是在一个特定的线程中执行,这个线程在初始化的时候就开启一个runloop。
+ (
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;
}
@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;
}
SDWebImage框架中,没有特定某个线程来执行connection,而是让operationqueue来分配线程执行,所以在connection start的时候,会开启当前线程的runloop
if
(
floor
(
NSFoundationVersionNumber
) <=
NSFoundationVersionNumber_iOS_5_1
) {
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 10 , false );
}
else {
CFRunLoopRun ();
}
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 10 , false );
}
else {
CFRunLoopRun ();
}
手动调度函数有下面三个,前两个是设置/取消connection 回调方法所在的runloop中,第三个直接设置一个operationQueue,将connection的delegate的回调方法交给 queue来调度
- (
void
)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode NS_AVAILABLE(
10
_5,
2
_0);
- ( void )unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode NS_AVAILABLE( 10 _5, 2 _0);
- ( void )setDelegateQueue:( nullable NSOperationQueue*) queue NS_AVAILABLE( 10 _7, 5 _0);
- ( void )unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode NS_AVAILABLE( 10 _5, 2 _0);
- ( void )setDelegateQueue:( nullable NSOperationQueue*) queue NS_AVAILABLE( 10 _7, 5 _0);
(2)start和cancel
start的方法调用时机,是NSURLConnection创建时startImmediately为NO的时候,要手动调用;cancel方法,提供给我们一个手动结束这个网络连接。-cancel方法,是理论上终止这个网络连接,但是也有可能不起任何作用,deleagte回调方法还会继续;但是如果确定被cancel,delegate就会被释放。
(3)canHandleRequest方法
这个方法,是判断能否通过一个request实例化一个connection。处理方式,是判断这个request能否被请求,正式发送之前的例行检查。
(4)两个category
两个category提供了两个类方法,同步加载以及异步加载,简单处理结果(block回调的形式)。代码如下,第一个是同步加载,会阻塞当前线程;第二个是异步加载,同时会有一个回调block,处理成功的data或者是失败的error
+ (
nullable
NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse *
__nullable
*
__nullable
)response error:(NSError **)error
+ (
void
)sendAsynchronousRequest:(NSURLRequest*) request
queue:(NSOperationQueue*) queue
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse* __nullable response, NSData* __nullable data, NSError* __nullable connectionError)) handler
2. NSURLConnection Delegate
(1)NSURLConnectionDelegate
- (
void
)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- ( BOOL )connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
- ( BOOL )connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
如上代码:第一个方法是在connection发生error后进行的回调,(最多回调一次),只要进行回调之后,delegate之后就不会再有回调的方法;第二个是询问connection实例要不要
CredentialStorage,不管返回是Y/N,都三个回调都会进行调用的;都三个是connection连接需要进行认证相关的操作回调
(2)NSURLConnectionDataDelegate
- (
nullable
NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(
nullable
NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (
void
)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (
nullable
NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request;
- ( void )connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten
totalBytesWritten:(NSInteger)totalBytesWritten
- ( void )connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten
totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
- (
nullable
NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
如上代码:该delegate是将网络数据存储在内存中的回调。第一个方法,是在需要改变request(重定向)的时候,返回一个request来进行继续加载,当然也可以返回nil(connection就会被终止,delegate回调会继续直到finish/error,一般情况下是error);第二个方法,是在收到服务器的响应时进行调用的;第三个方法,是获得网络数据的回调(可能多次调用);第四个方法,是在需要给request增加一些请求body的情况下进行调用的(连接错误或者认证错误),返回nil将会cancel这个connection;第五个方法,是在上传过程中,提供的process回调,(可以计算百分比);第六个方法,是要不要将这个connection的response缓存起来,下次调用可以直接从缓存拿(返回nil,就不能拿;如果进行缓存了,request的参数设置成可以从缓存拿,才会拿着个缓存);最后一个方法,是connection完成的回调。
(3)NSURLConnectionDownloadDelegate
- (
void
)connection:(NSURLConnection *)connection didWriteData:(
long
long
)bytesWritten totalBytesWritten:(
long
long
)totalBytesWritten expectedTotalBytes:(
long
long
) expectedTotalBytes;
- (void)connectionDidResumeDownloading:(NSURLConnection *)connection totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes;
- (
void
)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *) destinationURL;
如上代码:该delegate是将网络数据存储在文件中的回调。第一个方法,是获取当前写到文件中的进度;第二个方法是获取断点续传的进度;最后一个方法是必须要实现的,连接结束,提供了暂时存储文件的临时URL,我们需要的是将这个文件的数据转移(如果需要),之后这个临时文件就会被释放。