iOS網(wǎng)絡(luò)層常用的庫如ASIHTTPRequest,AFNetworking,MKNetworkKit等知名的第三方庫。隨著ASI不再更新许师,樓主基本上也跟著大部隊(duì)用了AF。AF用的是Cocoa層的API-NSURLConnection僚匆。
以前只是簡簡單單的用過NSURLConnection微渠,很多相關(guān)的方法都不是很熟悉,今天抽空了系統(tǒng)的學(xué)習(xí)了下咧擂,晚上順道總結(jié)下NSURLConnection的用法逞盆。
一、NSURLConnection的屬性及方法松申。
進(jìn)入NSURLConnection.h云芦,自上而下介紹所有的方法。
@interface NSURLConnection :
NSObject
{
@private
NSURLConnectionInternal *_internal;
}
/* Designated initializer */
/*
創(chuàng)建一個NSURLConnection贸桶,只建立連接舅逸,并沒有下載數(shù)據(jù)
request: 請求內(nèi)容
delegate:NSURLConnectionDelegate,NSURLConnection實(shí)例會強(qiáng)引用delegate皇筛,直到回調(diào)didFinishLoading,didFailWithError
NSURLConnection.cancel調(diào)用.(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 : 是否立即下載數(shù)據(jù),YES立即下載琉历,并把connection加入到當(dāng)前的runloop中。NO水醋,只建立連接旗笔,不下載數(shù)據(jù),需要手動
【connection start】開始下載數(shù)據(jù)拄踪。
*/
- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate
startImmediately:(BOOL)startImmediately
NS_AVAILABLE(10_5,
2_0);
/*
其實(shí)就是用的[self initWithRequest:request delegate:delegate startImmediately:YES];
不需要顯示的在去調(diào)用【connection start】
*/
- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;
/*
其實(shí)就是用的[self initWithRequest:request delegate:delegate startImmediately:YES];
不需要顯示的在去調(diào)用【connection start】
*/
+ (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;
/*
建立連接時用的請求
*/
@property (readonly,
copy) NSURLRequest *originalRequest
NS_AVAILABLE(10_8,
5_0);
/*
建立連接的請求進(jìn)過認(rèn)證協(xié)議可能會改變
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 *currentRequest
NS_AVAILABLE(10_8,
5_0);
/*
開始下載數(shù)據(jù)蝇恶,通過- (instancetype)initWithRequest:(NSURLRequest
*)request delegate:(id)delegate startImmediately:(BOOL)startImmediately
初始化的實(shí)例,調(diào)用【connection start】
*/
- (void)start
NS_AVAILABLE(10_5,
2_0);
/*
斷開網(wǎng)絡(luò)連接惶桐,取消請求撮弧,cancel方法不能保證代理回調(diào)立即不會調(diào)用(應(yīng)該是請求到的數(shù)據(jù),只能傳給代理)姚糊,cancel會release delegate
*/
- (void)cancel;
/*
將connection實(shí)例回調(diào)加入到一個runloop贿衍,NSURLConnectionDelegate回調(diào)會在這個runloop中響應(yīng)
注意該方法不能跟setDelegateQueue同時設(shè)置,只能選擇一個叛拷。
*/
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
NS_AVAILABLE(10_5,
2_0);
/*
取消在這個runloop中的回調(diào)
*/
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
NS_AVAILABLE(10_5,
2_0);
/*
如果設(shè)置了queue,回調(diào)將會在這個queue上進(jìn)行岂却,回調(diào)一次就類似與生成了一個NSBlockOperation加入到了queue中
注意該方法不能跟scheduleInRunLoop同時設(shè)置忿薇,只能選擇一個裙椭。
*/
- (void)setDelegateQueue:(NSOperationQueue*) queueNS_AVAILABLE(10_7,5_0);
@interface NSURLConnection (NSURLConnectionSynchronousLoading)
/*
類方法創(chuàng)建一個同步請求。這個方法是建立在異步的基礎(chǔ)上署浩,然后阻塞當(dāng)前線程實(shí)現(xiàn)的
response:響應(yīng)頭信息揉燃,傳遞一個二維指針
error:請求結(jié)果的狀態(tài)
*/
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse
**)response error:(NSError **)error;
@end
@interface NSURLConnection (NSURLConnectionQueuedLoading)
/*
發(fā)起一個異步請求
queue:completionHandler會運(yùn)行在這個queue中
completionHandler:請求回調(diào)block
*/
+ (void)sendAsynchronousRequest:(NSURLRequest*) request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse* response,
NSData* data, NSError* connectionError)) handler
NS_AVAILABLE(10_7,
5_0);
@end
二、NSURLConnection用法
上邊方法介紹的差不多了筋栋,寫幾個小demo試試炊汤。
首先定義一些基本配置
static char *
const URLSTRING =
"http://f.hiphotos.baidu.com/image/h%3D200/sign=a1217b1330fa828bce239ae3cd1f41cd/0e2442a7d933c895cc5c676dd21373f082020081.jpg";
-(NSURLRequest*)request{
NSString* urlString = [NSString
stringWithUTF8String:URLSTRING];
NSURL* url = [NSURL
URLWithString:urlString];
NSURLRequest* request = [NSURLRequest
requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:30.f];
return request;
}
另外,抽象出來了一個NSURLConnectionDelegate類來實(shí)現(xiàn)NSURLConnectionDelegate弊攘,這樣其他地方在用到NSURLConnection的時候就不需要在寫一大堆協(xié)議了抢腐。
#import
typedef
void(^NSURLConnectionCompeletionBlock)(id);
@interface NSURLConnectionDelegate :NSObject
@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.os
propertyForKey:NSStreamDataWrittenToMemoryStreamKey]);
}
[self.os
close];
_isFinish =
YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse
*)responsez{
if ([responsez isKindOfClass:[NSHTTPURLResponse
class]]) {
NSHTTPURLResponse* hr = (NSHTTPURLResponse*)responsez;
if (hr.statusCode ==
200) {
_contentLength = hr.expectedContentLength;
//? ? ? ? ? ? _os = [NSOutputStream
outputStreamToFileAtPath:[NSHomeDirectory()
stringByAppendingPathComponent:@"Documents/image.jpg"] append:NO];
_os = [NSOutputStream
outputStreamToMemory];
[_os
open];
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError
*)error{
if (self.completionBlock) {
NSError* error = [NSError
errorWithDomain:error.domain
code:error.code
userInfo:error.userInfo];
self.completionBlock(error);
}
_isFinish =
YES;
[self.os
close];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData
*)data{
if (!self.buffer) {
_buffer = [NSMutableData
dataWithCapacity:self.contentLength];
}
[self.buffer
appendData: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.os
write:&datas[totalWirte]
maxLength:(totalLength - totalWirte)];
if (writeLength == -1) {
[connection
cancel];
break;
}
totalWirte += writeLength;
NSLog(@"totalLenght = %lu , totalWirte = %lu",totalLength,totalWirte);
}
}
配合寫個NSOperation
#import
@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 = [super
init];
if (self) {
self.request = request;
}
return
self;
}
- (void)start{
if (!self.thread) {
_thread = [NSThread
currentThread];
}
self.finished =
NO;
self.executing =
YES;
__weak
NSURLConnectionOperation* wkSelf = self;
NSURLConnectionDelegate* delegate = [NSURLConnectionDelegate
new];
delegate.completionBlock = ^(id data){
if (!wkSelf.buffer) {
wkSelf.buffer = data;
}
//廣播通知,執(zhí)行completionBlock
wkSelf.finished =
YES;
wkSelf.executing =
NO;
};
_connection = [[NSURLConnection
alloc] initWithRequest:self.request
delegate:delegate
//保持delegate強(qiáng)引用
startImmediately:NO];
//start前手動設(shè)置runloop為默認(rèn)
[_connection
scheduleInRunLoop:[NSRunLoop
currentRunLoop] forMode:NSDefaultRunLoopMode];
[_connection
start];
while (!self.isFinished) {
[[NSRunLoop
currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate
distantFuture]];
}
NSLog(@"start end!!");
}
- (void)startAsync{
_thread = [[NSThread
alloc] initWithTarget:self
selector:@selector(start)
object:nil];
[self.thread
start];
}
- (void)setFinished:(BOOL)finished{
[self
willChangeValueForKey:@"isFinished"];
_finished = finished;
[self
didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing{
[self
willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self
didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent{
return
YES;
}
@end
同步請求:
-(IBAction)sync:(id)sender{
[self
printGO];
//
NSURLResponse* response =
nil;
NSError* error = nil;
NSData* data = [NSURLConnection
sendSynchronousRequest:[self
request]
returningResponse:&response
error:&error];
NSLog(@"get sync data!");
if ([response isKindOfClass:[NSHTTPURLResponse
class]]) {
NSHTTPURLResponse* hr = (NSHTTPURLResponse*)response;
if (hr.statusCode ==
200) {
NSLog(@"sync repsonse head: \n%@",hr.allHeaderFields);
self.imageView.image = [UIImage
imageWithData:data];
}
}
[self
printEnd];
}
-(IBAction)sync:(id)sender{
[self
printGO];
NSURLConnectionOperation* operation = [[NSURLConnectionOperation
alloc]
initWithRequest:[self
request]];
__weak NSURLConnectionOperation* wkOp = operation;
operation.completionBlock = ^{
__strong
NSURLConnectionOperation* sOp = wkOp;//防止wkOp被釋放襟交,強(qiáng)引用
NSLog(@"set ?");
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage
imageWithData:sOp.buffer];
});
};
[operation
start];
[self
printEnd];
}
異步請求:
-(IBAction)async:(id)sender{
[self
printGO];
//*************************** NSURLConnection async
//該方法只能簡單發(fā)起請求迈倍,等待結(jié)果,統(tǒng)計(jì)進(jìn)度不方便捣域。
[NSURLConnection
sendAsynchronousRequest:[self
request]
queue:self.queue
//completionHandler run on? this queue
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError) {
if ([response isKindOfClass:[NSHTTPURLResponse
class]]) {
NSHTTPURLResponse* hr = (NSHTTPURLResponse*)response;
if (hr.statusCode ==
200) {
[self
printCurrentThread];
self.imageView.image = [UIImage
imageWithData:data];
}
}
}];
[self
printEnd];
}
-(IBAction)async:(id)sender{
[self
printGO];
NSURLConnectionDelegate* connectionDelegate = [NSURLConnectionDelegate
new];
connectionDelegate.completionBlock = ^(id data){
[self
printCurrentThread];
if ([data isKindOfClass:[NSData
class]]) {
[[NSOperationQueue
mainQueue] addOperationWithBlock:^{
self.imageView.image = [UIImage
imageWithData:data];
}];
}
};
NSURLConnection* connection = [[NSURLConnection
alloc] initWithRequest:[self
request] delegate:connectionDelegate];
//delegate回調(diào)在當(dāng)前operationqueue開辟的線程中完成
//? ? [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection
setDelegateQueue:self.queue];
[connection
start];
[self
printEnd];
}