iOS网络层常用的库如ASIHTTPRequest,AFNetworking,MKNetworkKit等知名的第三方库。随着ASI不再更新,楼主基本上也跟着大部队用了AF。AF用的是Cocoa层的API-NSURLConnection。
以前只是简简单单的用过NSURLConnection,很多相关的方法都不是很熟悉,今天抽空了系统的学习了下,晚上顺道总结下NSURLConnection的用法。
一、NSURLConnection的属性及方法。
进入NSURLConnection.h,自上而下介绍所有的方法。
@interface NSURLConnection :NSObject
{
@private
NSURLConnectionInternal *_internal;
}
/* Designated initializer */
/*
创建一个NSURLConnection,只建立连接,并没有下载数据
request: 请求内容
delegate:NSURLConnectionDelegate,NSURLConnection实例会强引用delegate,直到回调didFinishLoading,didFailWithErrorNSURLConnection.cancel调用.(During the download the connection maintains a strong reference to the
delegate
. It releases that strong reference when the connection finishes loading, fails, or is canceled)
startImmediately : 是否立即下载数据,YES立即下载,并把connection加入到当前的runloop中。NO,只建立连接,不下载数据,需要手动
【connection start】开始下载数据。
*/
- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately NS_AVAILABLE(10_5, 2_0);
/*
其实就是用的[self initWithRequest:request delegate:delegate startImmediately:YES];
不需要显示的在去调用【connection start】
*/
- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;
/*
其实就是用的[self initWithRequest:request delegate:delegate startImmediately:YES];
不需要显示的在去调用【connection start】
*/
+ (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;
/*
建立连接时用的请求
*/
@property (readonly,copy) NSURLRequest *originalRequestNS_AVAILABLE(10_8,5_0);
/*
建立连接的请求进过认证协议可能会改变
As the connection performs the load,
this request may change as a result of protocol
canonicalization or due to following redirects.
-currentRequest can be used to retrieve this value.
*/
@property (readonly,copy) NSURLRequest *currentRequestNS_AVAILABLE(10_8,5_0);
/*
开始下载数据,通过- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately 初始化的实例,调用【connection start】
*/
- (void)startNS_AVAILABLE(10_5,2_0);
/*
断开网络连接,取消请求,cancel方法不能保证代理回调立即不会调用(应该是请求到的数据,只能传给代理),cancel会release delegate
*/
- (void)cancel;
/*
将connection实例回调加入到一个runloop,NSURLConnectionDelegate回调会在这个runloop中响应
注意该方法不能跟setDelegateQueue同时设置,只能选择一个。
*/
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)modeNS_AVAILABLE(10_5,2_0);
/*
取消在这个runloop中的回调
*/
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)modeNS_AVAILABLE(10_5,2_0);
/*
如果设置了queue,回调将会在这个queue上进行,回调一次就生成了一个NSBlockOperation加入到了queue中
注意该方法不能跟scheduleInRunLoop同时设置,只能选择一个。
*/
- ( void)setDelegateQueue:( NSOperationQueue*) queue NS_AVAILABLE( 10_7, 5_0);
@interface NSURLConnection (NSURLConnectionSynchronousLoading)
/*
类方法创建一个同步请求。这个方法是建立在异步的基础上,然后阻塞当前线程实现的
response:响应头信息,传递一个二维指针
error:请求结果的状态
*/
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
@end
@interface NSURLConnection (NSURLConnectionQueuedLoading)
/*
发起一个异步请求
queue:completionHandler会运行在这个queue中
completionHandler:请求回调block
*/
+ (void)sendAsynchronousRequest:(NSURLRequest*) request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse* response,NSData* data, NSError* connectionError)) handlerNS_AVAILABLE(10_7,5_0);
@end
二、NSURLConnection用法
首先定义一些基本配置
static char *const URLSTRING ="http://f.hiphotos.baidu.com/image/h%3D200/sign=a1217b1330fa828bce239ae3cd1f41cd/0e2442a7d933c895cc5c676dd21373f082020081.jpg";
-(NSURLRequest*)request{
NSString* urlString = [NSStringstringWithUTF8String:URLSTRING];
NSURL* url = [NSURLURLWithString:urlString];
NSURLRequest* request = [NSURLRequestrequestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:30.f];
return request;
}
另外,抽象出来了一个NSURLConnectionDelegate类来实现NSURLConnectionDelegate,这样其他地方在用到NSURLConnection的时候就不需要在写一大堆协议了。
#import <Foundation/Foundation.h>
typedefvoid(^NSURLConnectionCompeletionBlock)(id);
@interface NSURLConnectionDelegate :NSObject<NSURLConnectionDataDelegate>
@property(nonatomic ,strong , readonly)NSOutputStream* os;
@property(nonatomic ,assign , readonly)BOOL isFinish;
@property(nonatomic ,strong , readonly)NSMutableData* buffer;
@property(nonatomic ,assign , readonly)NSUInteger contentLength;
@property(nonatomic ,strong) NSURLConnectionCompeletionBlock completionBlock;
@end
#import "NSURLConnectionDelegate.h"
@implementation NSURLConnectionDelegate
- (void)dealloc
{
NSLog(@"__%s__",__FUNCTION__);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (self.completionBlock) {
self.completionBlock([self.ospropertyForKey:NSStreamDataWrittenToMemoryStreamKey]);
}
[self.osclose];
_isFinish = YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)responsez{
if ([responsez isKindOfClass:[NSHTTPURLResponseclass]]) {
NSHTTPURLResponse* hr = (NSHTTPURLResponse*)responsez;
if (hr.statusCode ==200) {
_contentLength = hr.expectedContentLength;
// _os = [NSOutputStream outputStreamToFileAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/image.jpg"] append:NO];
_os = [NSOutputStreamoutputStreamToMemory];
[_osopen];
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
if (self.completionBlock) {
NSError* error = [NSErrorerrorWithDomain:error.domain
code:error.code
userInfo:error.userInfo];
self.completionBlock(error);
}
_isFinish = YES;
[self.osclose];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if (!self.buffer) {
_buffer = [NSMutableDatadataWithCapacity:self.contentLength];
}
[self.bufferappendData:data];
//
NSUInteger totalLength = data.length;
NSUInteger totalWirte = 0;
const uint8_t * datas = data.bytes;
while (totalWirte < totalLength) {
/*
The number of bytes actually written, or -1 if an error occurs. More information about the error can be obtained with streamError. If the receiver is a fixed-length stream and has reached its capacity, 0 is returned.
*/
NSInteger writeLength = [self.oswrite:&datas[totalWirte] maxLength:(totalLength - totalWirte)];
if (writeLength == -1) {
[connectioncancel];
break;
}
totalWirte += writeLength;
NSLog(@"totalLenght = %lu , totalWirte = %lu",totalLength,totalWirte);
}
}
配合写个NSOperation
#import <Foundation/Foundation.h>
@interface NSURLConnectionOperation :NSOperation
@property(nonatomic ,strong) NSURLRequest* request;
@property(nonatomic ,strong ) NSData* buffer;
-(instancetype)initWithRequest:(NSURLRequest*)request;
-(void)startAsync;
@end
#import "NSURLConnectionOperation.h"
#import "NSURLConnectionDelegate.h"
@interface NSURLConnectionOperation ()
@property (nonatomic,assign, getter=isExecuting)BOOL executing;
@property (nonatomic,assign, getter=isFinished)BOOL finished;
@property (nonatomic,assign, getter=isConcurrent)BOOL concurrent;
@property(nonatomic ,strong ) NSThread* thread;
@property(nonatomic ,strong ) NSURLConnection* connection;
@end
@implementation NSURLConnectionOperation
@synthesize executing=_executing,finished=_finished,concurrent=_concurrent;
- (void)dealloc
{
NSLog(@"%s",__FUNCTION__);
}
-(instancetype)initWithRequest:(NSURLRequest *)request{
self = [superinit];
if (self) {
self.request = request;
}
return self;
}
- (void)start{
if (!self.thread) {
_thread = [NSThreadcurrentThread];
}
self.finished =NO;
self.executing =YES;
__weakNSURLConnectionOperation* wkSelf = self;
NSURLConnectionDelegate* delegate = [NSURLConnectionDelegatenew];
delegate.completionBlock = ^(id data){
if (!wkSelf.buffer) {
wkSelf.buffer = data;
}
//广播通知,执行completionBlock
wkSelf.finished =YES;
wkSelf.executing =NO;
};
_connection = [[NSURLConnectionalloc] initWithRequest:self.request
delegate:delegate //保持delegate强引用
startImmediately:NO];
//start前手动设置runloop为默认
[_connectionscheduleInRunLoop:[NSRunLoopcurrentRunLoop] forMode:NSDefaultRunLoopMode];
[_connection start];
while (!self.isFinished) {
[[NSRunLoopcurrentRunLoop] runMode:NSDefaultRunLoopModebeforeDate:[NSDatedistantFuture]];
}
NSLog(@"start end!!");
}
- (void)startAsync{
_thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(start)object:nil];
[self.threadstart];
}
- (void)setFinished:(BOOL)finished{
[selfwillChangeValueForKey:@"isFinished"];
_finished = finished;
[selfdidChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing{
[selfwillChangeValueForKey:@"isExecuting"];
_executing = executing;
[selfdidChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent{
return YES;
}
@end
同步请求:
-(IBAction)sync:(id)sender{
[selfprintGO];
//
NSURLResponse* response = nil;
NSError* error = nil;
NSData* data = [NSURLConnectionsendSynchronousRequest:[selfrequest]
returningResponse:&response
error:&error];
NSLog(@"get sync data!");
if ([response isKindOfClass:[NSHTTPURLResponseclass]]) {
NSHTTPURLResponse* hr = (NSHTTPURLResponse*)response;
if (hr.statusCode ==200) {
NSLog(@"sync repsonse head: \n%@",hr.allHeaderFields);
self.imageView.image = [UIImageimageWithData:data];
}
}
[selfprintEnd];
}
-(IBAction)sync:(id)sender{
[selfprintGO];
NSURLConnectionOperation* operation = [[NSURLConnectionOperationalloc] initWithRequest:[selfrequest]];
__weak NSURLConnectionOperation* wkOp = operation;
operation.completionBlock = ^{
__strongNSURLConnectionOperation* sOp = wkOp;//防止wkOp被释放,强引用
NSLog(@"set ?");
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImageimageWithData:sOp.buffer];
});
};
[operationstart];
[selfprintEnd];
}
异步请求:
-(IBAction)async:(id)sender{
[selfprintGO];
//*************************** NSURLConnection async
//该方法只能简单发起请求,等待结果,统计进度不方便。
[NSURLConnectionsendAsynchronousRequest:[selfrequest]
queue:self.queue//completionHandler run on this queue
completionHandler:^(NSURLResponse *response,NSData *data, NSError *connectionError) {
if ([response isKindOfClass:[NSHTTPURLResponseclass]]) {
NSHTTPURLResponse* hr = (NSHTTPURLResponse*)response;
if (hr.statusCode ==200) {
[selfprintCurrentThread];
self.imageView.image = [UIImageimageWithData:data];
}
}
}];
[selfprintEnd];
}
-(IBAction)async:(id)sender{
[selfprintGO];
NSURLConnectionDelegate* connectionDelegate = [NSURLConnectionDelegatenew];
connectionDelegate.completionBlock = ^(id data){
[selfprintCurrentThread];
if ([data isKindOfClass:[NSDataclass]]) {
[[NSOperationQueuemainQueue] addOperationWithBlock:^{
self.imageView.image = [UIImageimageWithData:data];
}];
}
};
NSURLConnection* connection = [[NSURLConnectionalloc] initWithRequest:[selfrequest] delegate:connectionDelegate];
//delegate回调在当前operationqueue开辟的线程中完成
// [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connectionsetDelegateQueue:self.queue];
[connectionstart];
[selfprintEnd];
}