iOS网络开发编程之NSURLConnection详解

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*) queueNS_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用法

上边方法介绍的差不多了,写几个小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类来实现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];

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容