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

NSRecursiveLock递归锁的使用html

NSRecursiveLock实际上定义的是一个递归锁,这个锁能够被同一线程屡次请求,而不会引发死锁。这主要是用在循环或递归操做中。咱们先来看一个示例:node

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NSLock *lock = [[NSLock alloc] init];程序员

 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{web

 

    static void (^RecursiveMethod)(int);编程

 

    RecursiveMethod = ^(int value) {json

 

        [lock lock];安全

        if (value > 0) {服务器

 

            NSLog(@"value = %d", value);网络

            sleep(2);数据结构

            RecursiveMethod(value - 1);

        }

        [lock unlock];

    };

 

    RecursiveMethod(5);

});

这段代码是一个典型的死锁状况。在咱们的线程中,RecursiveMethod是递归调用的。因此每次进入这个block时,都会去加一次锁,而从第二次开始,因为锁已经被使用了且没有解锁,因此它须要等待锁被解除,这样就致使了死锁,线程被阻塞住了。调试器中会输出以下信息:

 

 

value = 5

*** -[NSLock lock]: deadlock ( '(null)')   *** Break on _NSLockError() to debug.

在这种状况下,咱们就可使用NSRecursiveLock。它能够容许同一线程屡次加锁,而不会形成死锁。递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操做。只有全部达到这种平衡,锁最后才能被释放,以供其它线程使用。

因此,对上面的代码进行一下改造,

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

 

Lock的使用:

@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;

 

self.lock = [[NSRecursiveLock alloc] init];

    self.lock.name = kAFNetworkingLockName;

[self.lock lock];

    if (!responseData){

        _responseData = nil;

    } else {

        _responseData = [NSData dataWithBytes:responseData.bytes length:responseData.length];

    }

[self.lock unlock];

有关IO的东西,要lock

 

 [self performSelector:@selector(method)]的使用http://blog.sina.com.cn/s/blog_9075354e0102v0u7.html

 

[self method] 与 [self performSelector:@selector(method)]之间的区别(原)

今天更新项目遇到一个问题致使主线程阻塞,导致陷入死循环,解决方案:改[self invoked]为 [self performSelector:  withObject:afterDelay:];故简述一下两者的区别:

 

performSelector

1.能够避免编译警告

2.performSelector的其余变体功能,能够用来延时调用和跨线程调用。

注意:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

 

在主线程上执行指定的方法,使用默认的模式(NSDefaultRunLoopMode)。

默认的模式指:主线程中的方法进行排队,是一个循环队列,而且循环执行。

参数:

aSelector:要在主线程执行的方法,该方法不能有返回值,而且只能有一个参数。

 

arg:要传递的参数,若是无参数,就设为nil

 

wait:要执行的aSelector方法,是否立刻执行。

若是设置为YES:等待当前线程执行完之后,主线程才会执行aSelector方法;

设置为NO:不等待当前线程执行完,就在主线程上执行aSelector方法。

若是,当前线程就是主线程,那么aSelector方法会立刻执行。

 

该方法用途:由于iPhone编程,对UI的修改,只能在主线程上执行。能够用该方法来完成UI的修改。

 

接着讲performSelector

http://ronglei0324.blog.163.com/blog/static/67633223201371233549313/

对performSelector:系列方法进行了一个用法的简单分析1.- (id)performSelector:(SEL)aSelector;- (id)performSelector:(SEL)aSelectorwithObject:(id)object;- (id)performSelector:(SEL)aSelectorwithObject:(id)object1 withObject:(id)object2;这三个方法,均为同步执行,与线程无关,主主线程和子一程中都可调用成功。等同于直接调用该方法。在须要动态的去调用方法的时候去使用。

例如:[selfperformSelector:@selector(test2)];与[self test2];执行效果上彻底相同。

2.-(void)performSelector:(SEL)aSelector withObject:(id)anArgumentafterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;-(void)performSelector:(SEL)aSelector withObject:(id)anArgumentafterDelay:(NSTimeInterval)delay;这两个方法为异步执行,即便delay传参为0,仍为异步执行。只能在主线程中执行,在子线程中不会调到aSelector方法。可用于当点击UI中一个按钮会触发一个消耗系统性能的事件,在事件执行期间按钮会一直处于高亮状态,此时能够调用该方法去异步的处理该事件,就能避免上面的问题。在方法未到执行时间以前,取消方法为:

+(void)cancelPreviousPerformRequestsWithTarget:(id)aTargetselector:(SEL)aSelector object:(id)anArgument;+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;

注意:调用该方法以前或在该方法所在的viewController生命周期结束的时候去调用取消函数,以确保不会引发内存泄露。

3.-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)argwaitUntilDone:(BOOL)wait modes:(NSArray *)array;- (void)performSelectorOnMainThread:(SEL)aSelectorwithObject:(id)arg waitUntilDone:(BOOL)wait;这两个方法,在主线程和子线程中都可执行,均会调用主线程的aSelector方法

若是设置wait为YES:等待当前线程执行完之后,主线程才会执行aSelector方法;

设置为NO:不等待当前线程执行完,就在主线程上执行aSelector方法。

若是,当前线程就是主线程,那么aSelector方法会立刻执行。

注意:apple不容许程序员在主线程之外的线程中对ui进行操做,此时咱们必须调用performSelectorOnMainThread函数在主线程中完成UI的更新

4.-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thrwithObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thrwithObject:(id)arg waitUntilDone:(BOOL)wait;调用指定线程中的某个方法。分析效果同3。

5.-(void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg起子线程在后台运行?????????????本身也有一些疑问。

 

观察AFURLConnectionOperation中的一个方法的例子:

- (void)pause {

    if ([self isPaused] || [self isFinished] || [self isCancelled]) {

        return;

    }

   

    [self.lock lock];

   

    if ([self isExecuting]) {

        [self performSelector:@selector(operationDidPause)onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];

       

        dispatch_async(dispatch_get_main_queue(), ^{

            NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

           [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];

        });

    }

   

    self.state = AFOperationPausedState;

   

    [self.lock unlock];

}

+ (NSThread *)networkRequestThread {

    static NSThread*_networkRequestThread = nil;

    static dispatch_once_toncePredicate;

    dispatch_once(&oncePredicate,^{

       _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];

       [_networkRequestThread start];

    });

   

    return_networkRequestThread;

}

apple不容许程序员在主线程之外的线程中对ui进行操做,此时咱们必须调用performSelectorOnMainThread或别的函数在主线程中完成UI的更新和kvo的消息tooltian

 

 

2.        状态机

继承 NSOperation 有个很麻烦的东西要处理,就是改变状态时须要发 KVO 通知,不然这个类加入 NSOperationQueue 不可用了。 NSOperationQueue 是用 KVO 方式侦听 NSOperation 状态的改变,以判断这个任务当前是否已完成,完成的任务须要在队列中除去并释放。

 

AFURLConnectionOperation 对此作了个状态机,统一搞定状态切换以及发 KVO 通知的问题,内部要改变状态时,就只须要相似 self.state =AFOperationReadyState 的调用而不须要作其余了,状态改变的 KVO 通知在 setState 里发出。

总的来讲状态管理相关代码就三部分,一是限制一个状态能够切换到其余哪些状态,避免状态切换混乱,二是状态 Enum值 与NSOperation 四个状态方法的对应,三是在 setState 时统一发 KVO 通知。详见代码注释。

 

相关代码:AFKeyPathFromOperationState,AFStateTransitionIsValid, -setState:, -isPaused:, -isReady:, -isExecuting:,-isFinished:.

例子:

  a.[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];

 

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

 

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

 

 b. - (void)cancelConnection{

    NSDictionary *userInfo= nil;

    if ([self.request URL]) {

        userInfo =[NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];

    }

    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];

 

    if (![self isFinished]) {

        if (self.connection) {

            [self.connection cancel];

            [self performSelector:@selector(connection:didFailWithError:)withObject:self.connection withObject:error];

        } else {

            // Accomodate race condition where `self.connection` hasnot yet been set before cancellation

            self.error = error;

            [self finish];

        }

    }

}

 

  c. - (void)finish {

    [self.lock lock];

    self.state = AFOperationFinishedState;

    [self.lock unlock];

 

    dispatch_async(dispatch_get_main_queue(), ^{

        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];

    });

}

 

3.         NSURLConnectionDelegate

处理 NSURLConnection Delegate 的内容很少,代码也是按请求回调的顺序排列下去,十分易读,主要流程就是接收到响应的时候打开 outputStream,接着有数据过来就往 outputStream 写,在上传/接收数据过程当中会回调上层传进来的相应的callback,在请求完成回调到connectionDidFinishLoading 时,关闭 outputStream,用 outputStream 组装 responseData 做为接收到的数据,把 NSOperation 状态设为 finished,表示任务完成,NSOperation 会自动调用 completeBlock,再回调到上层。

 

4.setCompleteBlock

NSOperation 在 iOS4.0 之后提供了个接口 setCompletionBlock,能够传入一个 block 做为任务执行完成时(state状态机变为finished时)的回调,AFNetworking直接用了这个接口,并经过重写加了几个功能:

 

A.消除循环引用

在 NSOperation 的实现里,completionBlock 是 NSOperation 对象的一个成员,NSOperation 对象持有着 completionBlock,若传进来的 block 用到了 NSOperation 对象,或者 block 用到的对象持有了这个 NSOperation 对象,就会形成循环引用。这里执行完 block 后调用 [strongSelfsetCompletionBlock:nil] 把 completionBlock 设成 nil,手动释放 self(NSOperation对象) 持有的 completionBlock 对象,打破循环引用。

 

能够理解成对外保证传进来的block必定会被释放,解决外部使用使很容易出现的因对象关系复杂致使循环引用的问题,让使用者不知道循环引用这个概念都能正确使用。

- (void)setCompletionBlock:(void (^)(void))block {

    [self.lock lock];

    if (!block) {

        [super setCompletionBlock:nil];

    } else {

        __weak __typeof(self)weakSelf = self;

        [super setCompletionBlock:^ {

            __strong __typeof(weakSelf)strongSelf= weakSelf;

 

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wgnu"

            dispatch_group_t group =strongSelf.completionGroup ?: url_request_operation_completion_group();

            dispatch_queue_t queue =strongSelf.completionQueue ?: dispatch_get_main_queue();

#pragma clang diagnostic pop

 

            dispatch_group_async(group, queue, ^{

               block();

            });

 

            dispatch_group_notify(group, url_request_operation_completion_queue(), ^{

               [strongSelf setCompletionBlock:nil];

            });

        }];

    }

    [self.lock unlock];

}

 

 

2.AFHTTPRequestOperation

AFHTTPRequestOperation 继承了 AFURLConnectionOperation,把它放一块儿说是由于它没作多少事情,主要多了responseSerializer,暂停下载断点续传,以及提供接口请求成功失败的回调接口 -setCompletionBlockWithSuccess:failure:。

AFHTTPRequestOperation 继承自 AFURLConnectionOperation,使用HTTP以及HTTPS协议来处理网络请求。它封装成了一个能够使人接受的代码形式。固然AFHTTPRequestOperationManager 目前是最好的用来处理网络请求的方式,但AFHTTPRequestOperation 也有它本身的用武之地。

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

NSURLRequest *request = [NSURLRequest requestWithURL:URL];

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

op.responseSerializer =[AFJSONResponseSerializer serializer];

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

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

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

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

}];

[[NSOperationQueuemainQueue] addOperation:op];

 

Batchof Operations( 许多操做一块儿进行 )

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:fileURLname:@"images[]" error:nil];

  }];

 

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

 

  [mutableOperationsaddObject:operation];

}

 

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

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

} completionBlock:^(NSArray *operations) {

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

}];

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

 

3.AFURLRequestSerialization

AFURLRequestSerialization用于帮助构建NSURLRequest,主要作了两个事情:

1.构建普通请求:格式化请求参数,生成HTTP Header。

2.构建multipart请求。

分别看看它在这两点具体作了什么,怎么作的。

 1.构建普通请求

 

A.格式化请求参数

通常咱们请求都会按key=value的方式带上各类参数,GET方法参数直接加在URL上,POST方法放在body上,NSURLRequest没有封装好这个参数的解析,只能咱们本身拼好字符串。AFNetworking提供了接口,让参数能够是NSDictionary, NSArray,NSSet这些类型,再由内部解析成字符串后赋给NSURLRequest。

 

第一部分是用户传进来的数据,支持包含NSArray,NSDictionary,NSSet这三种数据结构。

 

第二部分是转换成AFNetworking内本身的数据结构,每个key-value对都用一个对象AFQueryStringPair表示,做用是最后能够根据不一样的字符串编码生成各自的key=value字符串。主要函数是AFQueryStringPairsFromKeyAndValue,详见源码注释。

 

第三部分是最后生成NSURLRequest可用的字符串数据,而且对参数进行url编码,在AFQueryStringFromParametersWithEncoding这个函数里。

 

最后在把数据赋给NSURLRequest时根据不一样的HTTP方法分别处理,对于GET/HEAD/DELETE方法,把参数加到URL后面,对于其余如POST/PUT方法,把数据加到body上,并设好HTTP头,告诉服务端字符串的编码。

static NSString *AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncodingstringEncoding) {

    NSMutableArray*mutablePairs = [NSMutableArray array];

    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {

       [mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]];

    }

 

    return [mutablePairs componentsJoinedByString:@"&"];

}

 

NSArray *AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {

    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);

}

 

NSArray *AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {

    NSMutableArray*mutableQueryStringComponents = [NSMutableArray array];

 

    NSSortDescriptor*sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];

 

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

        NSDictionary*dictionary = value;

        // Sort dictionary keys to ensure consistent ordering inquery string, which is important when deserializing potentially ambiguoussequences, such as an array of dictionaries

        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[sortDescriptor ]]) {

            id nestedValue =[dictionary objectForKey:nestedKey];

            if (nestedValue) {

               [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];

            }

        }

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

        NSArray *array = value;

        for (id nestedValue in array) {

           [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];

        }

    } else if ([value isKindOfClass:[NSSet class]]) {

        NSSet *set = value;

        for (id obj in [set sortedArrayUsingDescriptors:@[sortDescriptor ]]) {

           [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];

        }

    } else {

        [mutableQueryStringComponentsaddObject:[[AFQueryStringPair alloc] initWithField:key value:value]];

    }

   

    returnmutableQueryStringComponents;

}

 

B.HTTP Header

AFNetworking帮你组装好了一些HTTP请求头,包括语言Accept-Language,根据 [NSLocalepreferredLanguages] 方法读取本地语言,高速服务端本身能接受的语言。还有构建 User-Agent,以及提供Basic Auth 认证接口,帮你把用户名密码作 base64 编码后放入 HTTP 请求头。详见源码注释。

 

C.其余格式化方式

HTTP请求参数不必定是要key=value形式,能够是任何形式的数据,能够是json格式,苹果的plist格式,二进制protobuf格式等,AFNetworking提供了方法能够很容易扩展支持这些格式,默认就实现了json和plist格式。详见源码的类AFJSONRequestSerializer和AFPropertyListRequestSerializer。

 

2.构建multipart请求

构建Multipart请求是占篇幅很大的一个功能,AFURLRequestSeria

 

Multipart是HTTP协议为web表单新增的上传文件的协议,协议文档是rfc1867,它基于HTTP的POST方法,数据一样是放在body上,跟普通POST方法的区别是数据不是key=value形式,key=value形式难以表示文件实体,为此Multipart协议添加了分隔符,有本身的格式结构,大体以下:lization里2/3的代码都是在作这个事。

—AaB03x

content-disposition: form-data;name=“name"

bang

--AaB03x

content-disposition: form-data;name="pic"; filename=“content.txt"

Content-Type: text/plain

... contents of bang.txt ...

--AaB03x--

 

以上表示数据name=bang以及一个文件,content.txt是文件名,… contents of bang.txt …是文件实体内容。分隔符—AaB03x是能够自定义的,写在HTTP头部里:

 

Content-type: multipart/form-data,boundary=AaB03x

 

每个部分都有本身的头部,代表这部分的数据类型以及其余一些参数,例如文件名,普通字段的key。最后一个分隔符会多加两横,表示数据已经结束:—AaB03x—。

 

 

NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];

    [mutableHeaderssetValue:[NSString stringWithFormat:@"form-data; name=\"%@\";filename=\"%@\"", name, fileName]forKey:@"Content-Disposition"];

    [mutableHeaderssetValue:mimeType forKey:@"Content-Type"];

   

    AFHTTPBodyPart *bodyPart= [[AFHTTPBodyPart alloc] init];

    bodyPart.stringEncoding = self.stringEncoding;

    bodyPart.headers =mutableHeaders;

    bodyPart.boundary = self.boundary;

    bodyPart.body = fileURL;

    bodyPart.bodyContentLength = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];

[self.bodyStream appendHTTPBodyPart:bodyPart];

 

B.实现

接下来讲说怎样构造Multipart里的数据,最简单的方式就是直接拼数据,要发送一个文件,就直接把文件全部内容读取出来,再按上述协议加上头部和分隔符,拼接好数据后扔给NSURLRequest的body就能够发送了,很简单。

 

构建本身的数据结构,只保存要上传的文件地址,边上传边拼数据,上传是分片的,拼数据也是分片的,拼到文件实体部分时直接从原来的文件分片读取。这方法没上述两种的问题,只是实现起来也没上述两种简单,AFNetworking就是实现这第三种方法,并且还更进一步,除了文件,还能够添加多个其余不一样类型的数据,包括NSData,和InputStream。

AFNetworking 里 multipart 请求的使用方式是这样:

▪  AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 

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

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

▪  [manager POST:@"http://example.com/resources.json" parameters:parameters constructingBodyWithBlock:^(id formData) { 

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

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

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

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

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

}]; 

 

[self.bodyStream appendHTTPBodyPart:bodyPart];

 

具体用处:

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.       AFSecurityPolicy

AFSecurityPolicy用于验证HTTPS请求的证书

 

NSURLConnection已经封装了https链接的创建、数据的加密解密功能,咱们直接使用NSURLConnection是能够访问 https网站的,但NSURLConnection并无验证证书是否合法,没法避免中间人攻击。要作到真正安全通信,须要咱们手动去验证服务端返回的 证书,AFSecurityPolicy封装了证书验证的过程,让用户能够轻易使用,除了去系统信任CA机构列表验证,还支持SSL  Pinning方式的验证。使用方法:

 

//把服务端证书(须要转换成cer格式)放到APP项目资源里,AFSecurityPolicy会自动寻找根目录下全部cer文件

AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];

securityPolicy.allowInvalidCertificates = YES;

[AFHTTPRequestOperationManager manager].securityPolicy = securityPolicy;

[manager GET:@"https://example.com/" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

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

}];

 

Allowing Invalid SSL Certificates( 容许不合法的SSL证书 )

AFHTTPRequestOperationManager *manager =[AFHTTPRequestOperationManager manager];

manager.securityPolicy.allowInvalidCertificates= YES; // not recommended for production

 

5.       AFURLSessionManager(开头有代码例子)

AFURLSessionManager  建立了一个管理一个NSURLSession的对象,基于一个指定的 NSURLSessionConfiguration 对象,它与如下的这些协议融合在一块儿( <NSURLSessionTaskDelegate> ,  <NSURLSessionDataDelegate> ,  <NSURLSessionDownloadDelegate> , <NSURLSessionDelegate> )。

Creating a Download Task( 建立一个下载任务 )

Creating an Upload Task for a Multi-PartRequest, with Progress( 建立一个复杂的请求,并附带进度 )

6.       AFHTTPRequestOperationManager  

将通用的与服务器应用交互的操做封装了起来,包括了请求的建立,回复的序列化,网络指示器的监测,安全性,固然还有请求操做的管理。

GET  Request( GET请求 )

POST  URL-Form-EncodedRequest( 附带表单编码的POST请求 )

POST  Multi-Part Request( 复杂的POST请求 )