文档章节

iOS开发 网络框架AFNetworking源码(一)

神补刀
 神补刀
发布于 2015/08/18 17:19
字数 1137
阅读 880
收藏 5

    目前iOS开发中使用最多的网络访问框架就是AFNetworking了。作为一个第三方框架,用起来确实比直接使用iOS自带的要方便得多。

    AFNetworking在github上可以直接下载。地址为:https://github.com/AFNetworking/AFNetworking 。

    首先先看AFURLConnectionOperation类,继承自NSOperation。

@interface
 AFURLConnectionOperation : 
NSOperation

    在这里构建了NSURLConnection,作为NSURLConnection的delegate处理请求回调,做好状态切换,线程管理,可以说是AFNetworking最核心的类。

    1.1这里用枚举定义了网络访问状态。

typedef NS_ENUM(NSInteger, AFOperationState) {
    AFOperationPausedState      = -1,
    AFOperationReadyState       = 1,
    AFOperationExecutingState   = 2,
    AFOperationFinishedState    = 3,
};

1.2为了保证线程安全,所有的单例都使用了dispatch_once,保证了其只执行一次,这种写法在iOS代码中随处可见,很普及。

static dispatch_group_t url_request_operation_completion_group() {
    static dispatch_group_t af_url_request_operation_completion_group;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_request_operation_completion_group = dispatch_group_create();
    });
    return af_url_request_operation_completion_group;
}
static dispatch_queue_t url_request_operation_completion_queue() {
    static dispatch_queue_t af_url_request_operation_completion_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    //这里的DISPATCH_QUEUE_CONCURRENT生成一个并发执行队列
        af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
    });
    return af_url_request_operation_completion_queue;
}

    1.3大量使用了: typedef 原变量类型 别名

    举个例子:typedef char(*pFun)(int)      使用时候:(*pFun)(2); 是不是很容易看懂?

    作者是这样子写的:

typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected);
typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge);
typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse);
typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse);

    你会看到在变量声明的时候,声明的block真心看起来很清爽,是不是?

@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse;

    1.4在代码块中避免循环应用:

- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
    [self.lock lock];
    if (!self.backgroundTaskIdentifier) {
        UIApplication *application = [UIApplication sharedApplication];
        __weak __typeof(self)weakSelf = self;
        self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if (handler) {
                handler();
            }
            if (strongSelf) {
                [strongSelf cancel];
                [application endBackgroundTask:strongSelf.backgroundTaskIdentifier];
                strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
            }
        }];
    }
    [self.lock unlock];
}

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

2.1、先看看网络请求发起方法,是调用了- (void)start开始的,接下来初始化connection链接。

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

初始化完成之后,,调用网络请求start方法,解开线程锁,然后回到主线程中发送一个通知给通知中心:

dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
    });

    看到了吗?作者发送通知的时候,使用的关键字,不是乱写的,很规范!

NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start";
NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";

2.2 关于代码中Runloop的操作,网上有一个解析,说得很清楚:

    若直接在主线程调用异步接口,会有个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];


AFNetworking 用的是在子线程中回调异步接口,创建了一条常驻线程专门处理所有请求的回调事件,这个模型跟 nodejs 有点类似。网络请求回调处理完,组装好数据后再给上层调用者回调,这时候回调是抛回主线程的,因为主线程是最安全的,使用者可能会在回调中更新UI,在子线程更新UI会导致各种问题,一般使用者也可以不需要关心线程问题。

2.3 AFURLConnectionOperation 有一把递归锁,在所有会访问/修改成员变量的对外接口都加了锁,因为这些对外的接口用户是可以在任意线程调用的,对于访问和修改成员变量的接口,必须用锁保证线程安全。

    后面这一部分是转载的。

        

© 著作权归作者所有

神补刀
粉丝 19
博文 78
码字总数 38937
作品 0
广州
程序员
私信 提问
OC使用AFNetWorking框架GET方法上传参数格式不是JSON串

iOS的APP端使用AFNetWorking这个框架连接服务器, 项目约定使用GET方法传参, 现在遇到问题是:iOS传的参数要组合成“字典”放入AFNetWorking,然后用AFNetWorking开始请求, 而AFNetWorking会...

Moses_Fu
2015/10/26
1.5K
0
iOS源码补完计划--AFNetworking 3.1.0源码研读

参拜一下AFNetworking的源码。 第四篇源码、暂时来看也是iOS方向的最后一篇、撸完准备趁着热乎撸一撸网络协议。 目录 准备工作 功能模块 AFURLSessionManager/AFHTTPSessionManager AFNetwo...

kirito_song
2018/05/25
0
0
iOS源码补完计划--AFNetworking(二)

目录 前言 AFNetworkReachabilityManager.h AFNetworkReachabilityManager.m API注释Demo 参考资料 前言 AFNetworking源码第二篇 主要看了看AFNetworkReachabilityManager的内容 作为一个辅助...

kirito_song
2018/05/16
0
0
iOS下的Http库AFNetworking

在iOS下开发一直是用ASIHTTPRequest库,ASIHTTPRequest已经停止更新,看了这个文章http://www.oschina.net/news/61416/github-top-100-objective-c-projects (原文https://github.com/Aufr......

vane_
2015/04/11
203
0
AFNetworking源码(一)

前言 AFNetworking是一个为 iOS 和 Mac OSX 制作的令人愉快的网络库,它建立在URL 装载系统框架的顶层,内置在Cocoa里,扩展了强有力的高级网络抽象。以最新的3.1.0版本读阅.本篇只对其基本使...

有毒的程序猿
2017/11/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

toast组件单元测试

先看是否存在 describe('Toast', () => { it('存在.', () => { expect(Toast).to.be.exist }) }); 看属性,我们要测 ToastVue 和 plugin.js describe('Toast', () =>......

ories
28分钟前
57
0
如何将整个MySQL数据库字符集和排序规则转换为UTF-8?

如何将整个MySQL数据库字符集转换为UTF-8并将排序规则转换为UTF-8? #1楼 在命令行外壳上 如果您是命令行外壳程序之一,则可以非常快速地执行此操作。 只需填写“ dbname”:D DB="dbname"(...

javail
今天
80
0
开源矿工系统内部的层

开源矿工系统内部的层 所谓“层”、“界”、“域”、“集合”,这些词其实是在试图表达物质系统的组成结构和运动景象中的规矩,这些不同人发明的词都是来源于对同一个规律的观察、发现、表达...

NTMiner
今天
88
0
如何将文件从一个git repo移到另一个(不是克隆),保留历史记录

我们的Git储存库是作为单个Monster SVN储存库的一部分开始的,其中每个项目都有自己的树,如下所示: project1/branches /tags /trunkproject2/branches /tags ...

技术盛宴
今天
65
0
数据结构之数组-c代码实现

在上一篇文章里讲了数组的具体内容,然后自己使用c语言对数组进行了实现。 其中定义了一个结构体,定义了长度、已使用长度和地址指针。 定义alloc函数来分配内存空间 之后便是插入元素的ins...

无心的梦呓
今天
65
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部