揭开A F N e t w o r k i n g 框 架 的神秘面纱 (上)

揭开A F N e t w o r k i n g 框 架 的神秘面纱
 揭开A F N e t w o r k i n g 框 架 的神秘面纱
 

 

使用AFNetworking这个网络库来进行开发会令你神清气爽。它创建在 FoundationURL Loading System 的上层,给Cocoa强大的网络库进行了高层次的扩展。它具备模块化的精良设计,囊括各类易用的API,亲,不用你就落伍了。php

最最使人感动的一点,不是这个网络库无所不能。而是,天天都有着许多的开发者齐心合力,共同维护更新这个网络库,为这个网络库贡献本身的一份力量。html

 

http://download.csdn.net/detail/u010123208/8433103(AFNetworking 官方文档+翻译  )node

 

http://www.boyunjian.com/v/softd/AFNetworking.html(AFNetworking iOS网络框架)ios

 

一、    为何要用AFNetworkingweb

http://wenku.baidu.com/link?url=Js9uLzEUl6NOLcjYnIoosWWiv2G6fXjyITKa96riF6SOEl_eoKH0QTLElCM1GV17adcm0xiDUv9a5BC_BMN81ydOCwfChsyEdShYdxzvybS(详情)json

 

   在ios开发中,通常状况下,简单的向某个web站点简单的页面提交请求并获取服务器的响应,用xcode自带的NSURLConnection是能胜任的。可是,在绝大部分下咱们所须要访问的web页面则是属于那种受到权限保护的页面,并非有一个简单的URL能够访问的。这就涉及到了Session和Cookie的处理了,在此时使用NSURLConnection也是可以达到要求的,只是其中处理起来的复杂度和难度就提高了。 xcode

为了更好的处理向Web站点的请求,包括处理Session,Cookie等细节问题,使用AFNetworking则是更好的选择,他能够用于发送HTTP请求,接收HTTP的响应,可是不会缓存服务器的响应,不能执行HTML页面中的JAvascript代码,同时,AFNetworking还内置支持JSON,plist文件和XML文件的解析,使用比较方便。 缓存

扩展:一、Session:中文有译做时域的,就是只某个客户端在访问服务器起到中止访问这一段的时间间隔被称为时域。 安全

   二、Cookie:由服务器发送给客服端,把Cookie的key:value值储存在本地文件夹下,当下次请求的时候可以直接发送Cookie得到权限验证服务器

 

 

2. AFNetworking的用法 (这里仅对经常使用的使用方法进行总结)

 

  AFNetworking是第三方的框架,因此须要开发者自行下载,安装。并在AFNetworking.h文件导入#import“AFHTTPRequestOpeartionManager.h ”,把AFNetworking.h头文件放入prefix文件中。 a、建立AFHTTPRequestOpeartionManger对象 

b、根据服务器内容的不一样,为AFHTTPRequestOpeartionManger对象指定不一样的解析器,该对象默认的解析器是JSON和Plist文件解析器。若是服务器的数据是XML格式则须要手动的更改解析器(安防就有)AFResponseSerializer

详情解答以下

 

c、发送GET请求:  用Manager对象调用 GET:parameters:success:failure:方法便可,success代码块和failue代码块在网络请求成功/失败事后调用。 

d、success:参数指定了代码块中处理服务器响应成功的正确数据,failue:参数指定了代码块中处理服务器响应失败的错误数据、 

 

AFResponseSerializer

 responseObject表明了返回的值,当网络返回json或者xml之类的数据时,它们的本质都是字符串,咱们须要经过必定操做才能把字符串转成但愿的对象,AFNetWorking经过设置AFHTTPRequestOperationManager对象的responseSerializer属性替咱们作了这些事。

  若是返回的数据时json格式,那么咱们设定

  manager.responseSerializer =[[AFJSONResponseSerializer alloc] init],responseObject就是得到的json的根对象(NSDictionary或者NSArray)

  若是返回的是plist格式咱们就用AFPropertyListResponseSerializer解析器

  若是返回的是xml格式咱们就用AFXMLParserResponseSerializer解析器,responseObject表明了NSXMLParser对像

  若是返回的是图片,可使用AFImageResponseSerializer

  若是只是但愿得到返回二进制格式,那么可使用AFHTTPResponseSerializer

  AFRequestSerializer

  AFHTTPRequestOperationManager还能够经过设置requestSeializer属性设置请求的格式

  有以下的请求地址与参数

  NSString *URLString =@"http://example.com";

  NSDictionary *parameters =@{@"foo": @"bar", @"baz": @[@1, @2, @3]};

  咱们使用AFHTTPRequestSerializer的GET提交方式

  [[AFHTTPRequestSerializerserializer] requestWithMethod:@"GET" URLString:URLStringparameters:parameters error:nil];

  GEThttp://example.com?foo=bar&baz[]=1&baz[]=2&baz[]=3

  咱们使用AFHTTPRequestSerializer的POST提交方式

  [[AFHTTPRequestSerializerserializer] requestWithMethod:@"POST" URLString:URLStringparameters:parameters];

  POST http://example.com/

  Content-Type:application/x-www-form-urlencoded

  foo=bar&baz[]=1&baz[]=2&baz[]=3

  咱们使用AFJSONRequestSerializer

  [[AFJSONRequestSerializerserializer] requestWithMethod:@"POST" URLString:URLStringparameters:parameters];

  POST http://example.com/

  Content-Type: application/json

  {"foo": "bar","baz": [1,2,3]}

  默认提交请求的数据是二进制(AFHTTPRequestSerializer)的,返回格式是JSON(AFJSONResponseSerializer)

 

AFHTTPRquestOperationManager 

包含了常见的HTTP访问web站点的模式,有建立请求,连续的响应,网络类型监视以及安全。

 

1.”GET”:

 

  AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManger m

anager];   

 //调用get方法   

    [manager GET:@“http://example.com/resources.json”parameters : parameters    

//加载成功的代码块,能够接收数据   

 success:^(AFHTTPRequestOperation *operation,id responseobject)]{   

 

NSLog(@“json“:%@”,responseObject);   

 

}failure:^(AFHTTPRequestOperation *operation,NSError *error){   

 

NSLog(@“Error:%@”,error);   

 

}];

 

2. “POST”:URL-Form-Encoded Request  URL编码请求类型 

  AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager mana

ger];   

 NSDictionary *parameters = @{@"foo": @"bar"};   

[manager POST:@"http://example.com/resources.json" parameters:parameters suc

cess:^(AFHTTPRequestOperation *operation, id responseObject) {   

 

NSLog(@"JSON: %@", responseObject);   

 

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {   

 

NSLog(@"Error: %@", error);   

 

}]; 

 

3.  提交POST请求时附带文件 

 

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager mana

ger];   

NSDictionary *parameters = @{@"foo": @"bar"};   

NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];   

[manager POST:@"http://example.com/resources.json" parameters:parameters con

structingBodyWithBlock:^(id<AFMultipartFormData> formData) {   

 

[formData appendPartWithFileURL:filePath name:@"image" error:nil];   

 

} success:^(AFHTTPRequestOperation *operation, id responseObject) {   

 NSLog(@"Success: %@", responseObject);   

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {  

 NSLog(@"Error: %@", error);   

}]; 

 

http://my.oschina.net/wangbin618/blog/491487?p=%7b%7bpage(详情)

 

也能够经过appendPartWithFileData上传NSData数据的文件

  AFHTTPRequestOperation

  除了使用AFHTTPRequestOperationManager访问网络外还能够经过AFHTTPRequestOperation,AFHTTPRequestOperation继承自AFURLConnectionOperation,

  AFURLConnectionOperation继承自NSOperation,因此能够经过AFHTTPRequestOperation定义了一个网络请求任务,而后添加到队列中执行。

NSMutableArray *mutableOperations = [NSMutableArray array];

for (NSURL *fileURL in filesToUpload) {

NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

[formData appendPartWithFileURL:fileURL name:@"images[]" error:nil];

}];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

[mutableOperations addObject:operation];

}

NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {

NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);

} completionBlock:^(NSArray *operations) {

NSLog(@"All operations in batch complete");

}];

[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

  批量任务处理

NSMutableArray *mutableOperations = [NSMutableArray array];

for (NSURL *fileURL in filesToUpload) {

NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

[formData appendPartWithFileURL:fileURL name:@"images[]" error:nil];

}];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

[mutableOperations addObject:operation];

}

NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {

NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);

} completionBlock:^(NSArray *operations) {

NSLog(@"All operations in batch complete");

}];

[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

 

 

 

4. 建立一个下载的任务

基于NSURLSession的API

AFURLSessionManager建立并完善了一个NSURLSession的对象基于听从NSURLSessionDelegate与NSURLSessionDataDelegate协议NSURLSessionConfigration对象。

 

 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaul

tSessionConfiguration];   

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionC

onfiguration:configuration];   

NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];   

NSURLRequest *request = [NSURLRequest requestWithURL:URL];  

 

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:re

quest progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)

 {   

 

    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDir

ectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];   

 

    return [documentsDirectoryURL URLByAppendingPathComponent:[response sugg

estedFilename]];

} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {   

 

  NSLog(@"File downloaded to: %@", filePath);   

}];   

[downloadTask resume]; 

 

5. 建立一个上传的任务

基于NSURLSession的API

 

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaul

tSessionConfiguration];   

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionC

onfiguration:configuration];   

NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];   

NSURLRequest *request = [NSURLRequest requestWithURL:URL];      

NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];   

NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request 

fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {   

if (error) {   

       NSLog(@"Error: %@", error);  

   } else {        

NSLog(@"Success: %@ %@", response, responseObject);   

}   

 }];   

 [uploadTask resume];

 

 

6.http://www.cocoachina.com/bbs/read.php?tid=184183(AFNetworking使用总结 一些总结)

一些特例

static NSString*const BaseURLString = @"http://www.raywenderlich.com/downloads/weather_sample/";   

NSString *weatherUrl = [NSStringstringWithFormat:@"%@weather.php?format=json",BaseURLString];     

NSURL *url = [NSURLURLWithString:weatherUrl];      

NSURLRequest *request = [NSURLRequestrequestWithURL:url];      

AFJSONRequestOperation *operation =      [AFJSONRequestOperationJSONRequestOperationWithRequest:request                                                success:^(NSURLRequest*request, NSHTTPURLResponse *response, id JSON) {                                                   //                                    NSDictionary*dicWeather = (NSDictionary *)JSON;                              NSLog(@"result:%@",dicWeather);                                               

}  failure:^(NSURLRequest*request, NSHTTPURLResponse *response, NSError *error, id JSON) {                                                   

 UIAlertView*alertView = [[UIAlertView alloc] initWithTitle:@"Error RetrievingWeather"                message:[NSStringstringWithFormat:@"%@",error] delegate:self     cancelButtonTitle:@"OK"  otherButtonTitles: nil];                                    [alertView show];                                                }];   

[operation start];   

▪  (1)根据基本的URL构造除完整的一个URL,而后经过这个完整的URL得到一个NSURL对象,而后根据这个url得到一个NSURLRequest。 

▪  (2)AFJSONRequestOperation是一个完整的类,整合了从网络中获取数据并对JSON进行解析。 

▪  (3)当请求成功,则运行成功块。在本例中,把解析出来的天气数据从JSON变量转换为一个字典(dictionary),并将其存储在字典中。 

(4)若是运行出问题了,则运行失败块(failure block),好比网络不可用。若是failure block被调用了,将会经过提示框显示错误信息。 

 

▪  重点:AFNetWorking异步加载图片 

(1)#import “UIImageView+AFNetworking.h” 

(2)UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(40, 80, 40, 40)];     

 __weak UIImageView *_imageView = imageView; 

 

 [imageView setImageWithURLRequest:[[NSURLRequest alloc] initWithURL:[NSURLURLWithString:@"http://www.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0001_sunny.png"]]        placeholderImage:[UIImage imageNamed:@"placeholder.png"]                             success:^(NSURLRequest *request,NSHTTPURLResponse *response, UIImage *image) {                                _

              imageView.image = image;    

▪      [_imageView setNeedsDisplay];                             }        failure:^(NSURLRequest *request, NSHTTPURLResponse*response, NSError *error) {                                ;                             }];      

▪  [self.view addSubview:imageView];   

 

 

7.xml的网络请求

 

1.xml类型的的接口

request:n<?xmlversion="1.0" encoding="utf-8"?>

<soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<soap:Body>

<UDCCUGetAlarmDetailxmlns="http://tempuri.org/ns-operator.xsd">

<sessionId>4961</sessionId>

<param><id>2490907771</id>

<nodeId>00-00-00-00-00-00-00-02-00-00-30-12-00-01-89</nodeId>

<sensorNo>6</sensorNo>

<time>1443502846</time>

</param>

</UDCCUGetAlarmDetail>

</soap:Body>

</soap:Envelope>

 

一下是对应网络请求的属性的封装:

NSDictionary *dicUser = [CommonUtil getObjectFromUD:@"userInfo"];

        if([CommonUtilisEmpty:dicUser[@"userid"]]){

            return;

        }

       

        NSDictionary *dicSessionid =[NSDictionary dictionaryWithObject:USERINFO_DICT[@"sessionid"]forKey:@"sessionId"];

        NSDictionary *dicNodeID =[NSDictionary dictionaryWithObject: self.model.nodeIdforKey:@"nodeId"];

        NSDictionary*dicSensorChannel = [NSDictionary dictionaryWithObject:self.model.sensor_channel forKey:@"sensorNo"];

        NSDictionary *dicTime =[NSDictionary dictionaryWithObject: self.model.sensor_timeforKey:@"time"];

        NSString *strMess =[NSString stringWithFormat: @"%@\r\n", self.feekBackTextView.text];

        NSDictionary *dicMess = [NSDictionarydictionaryWithObject: strMess forKey:@"message"];

       

        NSArray *dic = [NSArrayarrayWithObjects: dicNodeID, dicSensorChannel, dicTime, dicMess, nil];

       

        NSDictionary *param =[NSDictionary dictionaryWithObject: dic forKey:@"param"];

        NSArray * doc = [NSArrayarrayWithObjects: dicSessionid, param, nil];

 

2.xml接口拼装:

   1. NSDictionary *dic = [CommonUtil getObjectFromUD: @"userInfo"];

    if([CommonUtil isEmpty:dic[@"userid"]]){

        return;

    }

   

    NSDictionary*dicSessionid = [NSDictionary dictionaryWithObject:USERINFO_DICT[@"sessionid"] forKey:@"sessionId"];

    NSArray * doc = [NSArray arrayWithObjects:dicSessionid,nil];

    NSMutableURLRequest *request = [NSrequestHelper PostDateWithMethod:@"UDCCUQueryRecentAlarms" andParam:doc andAction:@"UDCCUQueryRecentAlarms"];

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    operation.responseSerializer = [AFXMLParserResponseSerializer serializer];

   

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {}

       

   2. + (NSMutableURLRequest *) PostDateWithMethod:(NSString *)method andParam:(id)param andAction:(NSString *) action{

    NSMutableString * post =[[ NSMutableString alloc ] init ] ;

   

    [ post appendString:

     @"<?xml version=\"1.0\"encoding=\"utf-8\"?>\n"

     "<soap:Envelopexmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""

     " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""

     "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"

     "<soap:Body>\n" ];

   

    [ post appendString:@"<"];

    [ post appendString:method];

    [ post appendString:[NSString stringWithFormat:@" xmlns=\"%@\">\n",OPERATOR_NAME_SPACE_URL]];

   

    if([param isKindOfClass:[NSDictionary class]]){

        id value = nil;

        NSString *key = [[param keyEnumerator] nextObject];

        if(key != nil){

            value =[param valueForKey:key];

            [post appendString:@"<"];

            [post appendString:key];

            [post appendString:@">"];

           

            if([value isKindOfClass:[NSArray class]]){

               [post appendString: [self makeXmlString:value]];

            }else{

               [post appendString:value];

            }

            [post appendString:@"</"];

            [post appendString:key];

            [post appendString:@">\n"];

        }

       

    }else if([param isKindOfClass:[NSArray class]]){

        for (NSDictionary *object in param) {

            [post appendString:[self makeXmlString:object]];

        }

    }else{

        NSLog(@"Param Type Err");

        return nil;

    }

   

   

    //    for(NSDictionary *dict in paramArray)

    //    {

    //        NSString*value = nil;

    //

    //        NSString*key = [[dict keyEnumerator] nextObject];

    //

    //        if (key!= nil)

    //        {

    //            value= [dict valueForKey:key];

    //

    //            [post appendString:@"<"];

    //            [post appendString:key];

    //            [post appendString:@">"];

    //            if(value != nil )

    //            {

    //                [post appendString:value];

    //            }

    //            else

    //            {

    //                [post appendString:@""];

    //            }

    //

    //            [post appendString:@"</"];

    //            [post appendString:key];

    //            [post appendString:@">\n"];

    //        }

    //    }

   

    [ post appendString:@"</"];

    [ post appendString:method];

    [ post appendString:@">\n"];

   

    [ post appendString:

     @"</soap:Body>\n"

     "</soap:Envelope>\n"

     ];

  

}

   3. //解析字典为XML格式

+ (NSString *) makeXmlString:(id) obj{

    NSMutableString *xmlString = [[NSMutableString alloc] initWithString:@""];

    if([obj isKindOfClass:[NSDictionary class]]){

        id value = nil;

        NSString *key = [[obj keyEnumerator] nextObject];

        if(key != nil){

            value = [obj valueForKey:key];

            [xmlString appendString:@"<"];

            [xmlString appendString:key];

            [xmlString appendString:@">"];

           

            if([value isKindOfClass:[NSArray class]]){

                [xmlString appendString: [self makeXmlString:value]];

            }else{

                [xmlString appendString:value];

            }

            [xmlString appendString:@"</"];

            [xmlString appendString:key];

            [xmlString appendString:@">\n"];

        }

       

    }else if([obj isKindOfClass:[NSArray class]]){

        for (NSDictionary *object in obj) {

            [xmlString appendString:[self makeXmlString:object]];

        }

    }

   

    return  xmlString;

   

}

 

3.这是一个典型的xml接口,属性变成xml,为了[request setHTTPBody:postData];

 next:

   NSData *postData =[[post description] dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];

    NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];

   

    NSString *soapAction = [NSString stringWithFormat:@"%@%@",OPERATOR_NAME_SPACE_URL , action];

   

    NSURL *url=[[NSURL alloc]initWithString:OPERATOR_WSDL_URL];

    NSMutableURLRequest  *request=[[NSMutableURLRequest alloc]init];

   

    [request setTimeoutInterval: 30];

    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];

    [request setURL: url] ;

    [request setHTTPMethod:@"POST"];

    [request setValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

    [request setValue:soapAction forHTTPHeaderField:@"SOAPAction"];

   

    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];

[request setHTTPBody:postData];

 

4.这是一个经典的网络请求request写法,如何结合afnetWorking网络请求:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

   operation.responseSerializer = [AFXMLParserResponseSerializer serializer];

   

   [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

       

               

 

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

 

       

    }];

   

[operation start];

 

AFNetworking源码解析

 

1.  AFURLConnectionOperation

 

AFURLConnectionOperation` is a subclass of`NSOperation` that implements `NSURLConnection` delegate methods.

AFURLConnectionOperation继承自NSOperation,是一个封装好的任务单元,在这里构建了NSURLConnection,做为NSURLConnection的delegate处理请求回调,作好状态切换,线程管理,能够说是AFNetworking最核心的类,下面分几部分说下看源码时注意的点,最后放上代码的注释。

 

 

经常使用技巧:

1.                   dispatch_once: 为保证线程安全,全部单例都用dispatch_once生成,保证只执行一次,这也是iOS开发经常使用的技巧。例如:

    static dispatch_queue_t af_url_request_operation_completion_queue; 

    static dispatch_once_t onceToken; 

    dispatch_once(&onceToken, ^{ 

        af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue",   DISPATCH_QUEUE_CONCURRENT ); 

    }); 

    return af_url_request_operation_completion_queue; 

2.                   weak & strong self: 常看到一个 block 要使用 self,会处理成在外部声明一个 weak 变量指向 self,在 block 里又声明一个 strong 变量指向 weakSelf:

__weak __typeof(self)weakSelf = self; 

self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ 

    __strong __typeof(weakSelf)strongSelf = weakSelf; 

}]; 

weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是由于一旦进入block执行,就不容许self在这个执行过程当中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。

 

 

1.        线程

先来看看 NSURLConnection 发送请求时的线程状况,NSURLConnection 是被设计成异步发送的,调用了start方法后,NSURLConnection 会新建一些线程用底层的 CFSocket 去发送和接收请求,在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件。

 

NSURLConnection 的同步方法 sendSynchronousRequest 方法也是基于异步的,一样要在其余线程去处理请求的发送和接收,只是同步方法会手动block住线程,发送状态的通知也不是经过 RunLoop 进行。

 

使用NSURLConnection有几种选择:

      A.在主线程调异步接口

若直接在主线程调用异步接口,会有个Runloop相关的问题:

 

当在主线程调用 [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES] 时,请求发出,侦放任务会加入到主线程的 Runloop 下,RunloopMode 会默认为 NSDefaultRunLoopMode。这代表只有当前线程的Runloop 处于 NSDefaultRunLoopMode 时,这个任务才会被执行。但当用户滚动 tableview 或 scrollview 时,主线程的 Runloop 是处于 NSEventTrackingRunLoopMode 模式下的,不会执行 NSDefaultRunLoopMode 的任务,因此会出现一个问题,请求发出后,若是用户一直在操做UI上下滑动屏幕,那在滑动结束前是不会执行回调函数的,只有在滑动结束,RunloopMode 切回 NSDefaultRunLoopMode,才会执行回调函数。苹果一直把动画效果性能放在第一位,估计这也是苹果提高UI动画性能的手段之一。

 

因此若要在主线程使用 NSURLConnection 异步接口,须要手动把 RunloopMode 设为 NSRunLoopCommonModes。这个 mode 意思是不管当前 Runloop 处于什么状态,都执行这个任务。

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 

[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 

[connection start]; 

 

B.在子线程调同步接口

若在子线程调用同步接口,一条线程只能处理一个请求,由于请求一发出去线程就阻塞住等待回调,须要给每一个请求新建一个线程,这是很浪费的,这种方式惟一的好处应该是易于控制请求并发的数量。

 

C.在子线程调异步接口

子线程调用异步接口,子线程须要有 Runloop 去接收异步回调事件,这里也能够每一个请求都新建一条带有 Runloop 的线程去侦听回调,但这一点好处都没有,既然是异步回调,除了处理回调内容,其余时间线程都是空闲可利用的,全部请求共用一个响应的线程就够了。

 

AFNetworking 用的就是第三种方式,建立了一条常驻线程专门处理全部请求的回调事件,这个模型跟 nodejs 有点相似。网络请求回调处理完,组装好数据后再给上层调用者回调,这时候回调是抛回主线程的,由于主线程是最安全的,使用者可能会在回调中更新UI,在子线程更新UI会致使各类问题,通常使用者也能够不须要关心线程问题。

 

如下是相关线程大体的关系,实际上多个 NSURLConnection 会共用一个 NSURLConnectionLoader 线程,这里就不细化了,除了处理 socket 的 CFSocket 线程,还有一些 Javascript:Core 的线程,目前不清楚做用,归为 NSURLConnection里的其余线程。由于 NSURLConnection 是系统控件,每一个iOS版本可能都有不同,能够先把 NSURLConnection 当成一个黑盒,只管它的 start 和 callback 就好了。若是使用 AFHttpRequestOperationManager 的接口发送请求,这些请求会统一在一个 NSOperationQueue 里去发,因此多了上面 NSOperationQueue 的一个线程。

相关代码:-networkRequestThread:,-start:, -operationDidStart:。