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

时间:2022-07-06 07:22:48
揭开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,亲,不用你就落伍了。

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

 

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

 

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

 

1、    为什么要用AFNetworking

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

 

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

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

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

   2、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:。