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,didFailWithError NSURLConnection.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 *originalRequest NS_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 *currentRequest NS_AVAILABLE(10_8, 5_0);
/*
開始下載數據,通過- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately 初始化的實例,調用【connection start】
*/
- (void)start NS_AVAILABLE(10_5, 2_0);
/*
斷開網絡連接,取消請求,cancel方法不能保證代理回調立即不會調用(應該是請求到的數據,只能傳給代理),cancel會release delegate
*/
- (void)cancel;
/*
將connection實例回調加入到一個runloop,NSURLConnectionDelegate回調會在這個runloop中響應
注意該方法不能跟setDelegateQueue同時設置,只能選擇一個。
*/
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode NS_AVAILABLE(10_5, 2_0);
/*
取消在這個runloop中的回調
*/
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode NS_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)) handler NS_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 = [NSString stringWithUTF8String:URLSTRING];
NSURL* url = [NSURL URLWithString:urlString];
NSURLRequest* request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:30.f];
return request;
}
另外,抽象出來了一個NSURLConnectionDelegate類來實現NSURLConnectionDelegate,這樣其他地方在用到NSURLConnection的時候就不需要在寫一大堆協議了。
#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;
}
//廣播通知,執行completionBlock
wkSelf.finished = YES;
wkSelf.executing = NO;
};
_connection = [[NSURLConnection alloc] initWithRequest:self.request
delegate:delegate //保持delegate強引用
startImmediately:NO];
//start前手動設置runloop為默認
[_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被釋放,強引用
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
//該方法只能簡單發起請求,等待結果,統計進度不方便。
[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回調在當前operationqueue開辟的線程中完成
// [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection setDelegateQueue:self.queue];
[connection start];
[self printEnd];
}