第三方框架源码解析

一,AFNetworking

这是 AFNetworking 发起一个 Get 请求的流程图,大概可以分为这几个步骤。

第三方框架源码解析_第1张图片
Get请求流程图

1,创建 NSMutableURLRequest对象,也就是设置request的请求头和请求体

   NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

2,创建NSURLSessionDataTask对象

 __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                NSLog(@" 6当前线程  %@",[NSThread currentThread]);
                success(dataTask, responseObject);
            }
        }
    }];

3,在创建NSURLSessionDataTask对象时是同步创建,防止创建对应的混乱,我们创建了一个任务task1 对应completionHandler1,然后又创建了task2 对应的completionHandler2,这时候在task2数据还没有返回的前提下,task1的数据返回了,就会调用completionHandler2,就是这样的一个bug,造成任务的创建是不安全的,不过这个问题已经在ios8后修复了。

    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });
static dispatch_queue_t url_session_manager_creation_queue() {
    static dispatch_queue_t af_url_session_manager_creation_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
    });

    return af_url_session_manager_creation_queue;
}

static void url_session_manager_create_task_safely(dispatch_block_t block) {
    if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
        // Fix of bug
        // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
        // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
        dispatch_sync(url_session_manager_creation_queue(), block);
    } else {
        block();
    }
}
创建AFURLSessionManagerTaskDelegate代理对象,用它接管了Foundation框架中NSURLSession/NSURLSessionTask/NSURLSessionDataTask/NSURLSessionDownloadTask等类中的NSURLSessionDelegate/NSURLSessionTaskDelegate/NSURLSessionDataDelegate/NSURLSessionDownloadDelegate等代理的回调     
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}
通过 NSURLSessionTask 的 taskIdentifier 标识符对 delegate 进行管理,只要是用于识别该NSURLSessionTask 的代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);
    [self.lock lock];
 self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

初始化NSURLSession对象时,设置了代理
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

KVO监听的属性值发生变化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            NSLog(@"countOfBytesReceived");
//这个是在Get请求下,网络响应过程中已经收到的数据量
            self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];//已经收到
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
              NSLog(@"countOfBytesExpectedToReceive");
//这个是在Get请求下,网络响应过程中期待收到的数据量
            self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];//期待收到
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
             NSLog(@"countOfBytesSent");
            self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];//已经发送
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
              NSLog(@"countOfBytesExpectedToSend");
            self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];//期待发送
        }
    }
    else if ([object isEqual:self.downloadProgress]) {
        //下载进度变化
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        //上传进度变化
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

4,响应回调的处理

等待系统代理方法的回调
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{

    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    [delegate URLSession:session dataTask:dataTask didReceiveData:data];

    if (self.dataTaskDidReceiveData) {
        self.dataTaskDidReceiveData(session, dataTask, data);
    }
}
创建一个并行队列用来管理数据的处理。目的是加快数据的处理速度。
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }

    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
//个人感觉内部使用dispatch_group_async用来异步派发执行completionHandler回调是为了留给调用者使用dispatch_group_notify等函数有机会监听completionHandler执行完成后的回调
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }

            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }

            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
#pragma clang diagnostic pop
}

二,AsyncDisplayKit:让iOS应用界面极致流畅
第一点理解ui操作必须在主线程上的原理(其实也可以在子线程进行,只是风险大):因为uikit不是线程安全的。两个线程同时设置同一个UIView的背景颜色,那么很有可能渲染显示的是颜色A,而此时在UIView逻辑树上的背景颜色属性为B。
两个线程同时操作view的树形结构:在线程A中for循环遍历并操作当前View的所有subView,然后此时线程B中将某个subView直接删除,这就 导致了错乱还可能导致应用崩溃。
第二点:UIView与CALayer的关系:
每个 UIView 内部都有一个 CALayer 在背后提供内容的绘制和显示。
uiview可以响应事件,calayer不能
在 View显示的时候,UIView 做为 Layer 的 CALayerDelegate,View 的显示内容由内部的 CALayer 的 display。
UIView 和 CALayer 都不是线程安全的,并且只能在主线程创建、访问和销毁。
第三AsyncDisplayKit:
原理理解,系统的界面更新过程,当有界面更新操作,先提交到一个全局容器中。runloop跑到BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 时,由于苹果注册了一个 Observer 监听runloop状态,会回调去执行提交到全局容器待处理的ui操作,先执行实际的更新和调整,在提交到gpu渲染到屏幕上。
而AsyncDisplayKit的更新过程:
它也在runloop注册了observer,同样监听runloop的BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) ,然后回调去执行在子线程完成的事物并提交到gpu渲染到屏幕上。
需要注意的是如果有文本和图片,也会在绘制阶段做一些优化,
1,将当前视图的子视图压缩成一层绘制在当前页面上
2,使用 - displayWithParameters:isCancelled: 返回一个 UIImage,对图像节点 ASImageNode 进行绘制
3,使用 - drawRect:withParameters:isCancelled:isRasterizing: 在 CG 上下文中绘制文字节点 ASTextNode
这三种方式都通过 ASDK 来优化视图的渲染速度。
注意:比系统更新少了绘制阶段,绘制阶段放到了子线程,绘制阶段对文本和图片做了优化,渲染时要快。
这篇文章介绍的很好:https://zhuanlan.zhihu.com/p/22255533?refer=iOS-Source-Code。
三个ASDK主要优化的方面:

  • 布局
    iOS自带的Autolayout在布局性能上存在瓶颈,并且只能在主线程进行计算。因此ASDK弃用了Autolayout。
  • 渲染
    对于大量文本,图片等的渲染,UIKit组件只能在主线程并且可能会造成GPU绘制的资源紧张。ASDK使用了一些方法,比如图层的预混合等,并且异步的在后台绘制图层,不阻塞主线程的运行。
  • 系统对象创建于销毁
    UIKit组件封装了CALayer图层的对象,在创建、调整、销毁的时候,都会在主线程消耗资源。ASDK自己设计了一套Node机制,也能够调用。
    ASDK最大的特点就是"异步"。
    将消耗时间的渲染、图片解码、布局以及其它 UI 操作等等全部移出主线程,这样主线程就可以对用户的操作及时做出反应,来达到流畅运行的目的。

三,FMDB
1,FMDatabaseQueue中创建了一个窜行队列,并使用dispatch_synco同步执行,来保证线程安全。
2,写入数据时需要保证只有一个FMDatabaseQueue实例对象
3,读取数据时可以使用FMDatabase来获取数据,不需要考虑线程安全.(文件数据库sqlite,同一时刻允许多个进程/线程读,但同一时刻只允许一个线程写).
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:@"/tmp/tmp.db"];
[queue inDatabase:^(FMDatabase *db) {
if ([db executeUpdate:@"INSERT INTO person VALUES (?, ?, ?)", @0, @"Demon", @20]) {
NSLog(@"Demon 插入成功 - %@", [NSThread currentThread]);
}
}];

四,MJExtension
设计一个NSObject的分类实现两个类方法
第一个类方法使用运行时获取属性的名称
第二个方法通过kvc设置属性的值

+ (NSArray *)yf_objcProperties
{
    NSArray *ptyList = objc_getAssociatedObject(self, kPropertyListKey);
        if (ptyList) {
                return ptyList;
            }
        unsigned int outCount = 0;
 objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
        NSMutableArray *mtArray = [NSMutableArray array];
        for (unsigned int i = 0; i < outCount; i++) {
                objc_property_t property = propertyList[i];
                const char *propertyName_C = property_getName(property);
                NSString *propertyName_OC = [NSString stringWithCString:propertyName_C encoding:NSUTF8StringEncoding];
                [mtArray addObject:propertyName_OC];
            }
        objc_setAssociatedObject(self, kPropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        free(propertyList);
        return mtArray.copy;
}
+ (instancetype)yf_objcWithDict:(NSDictionary *)dict
{
    id objc = [[self alloc]init];
    NSArray *propertyList = [self  yf_objcProperties];
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([propertyList containsObject:key]) {
            [objc setValue:obj forKey:key];
        }
    }];
    return objc;
}

五,uiview从绘制到渲染的过程 YYText

运行一段动画的过程可以分为6个阶段:
1> 布局(Layout)- 为视图/图层准备层级关系,以及设置图层属性(位置,背景色,边框等等)的阶段。调用layoutSubviews方法;
调用addSubview:方法;
2> 显示(Display) - 图层的寄宿图片被绘制的阶段。绘制涉及到-drawRect:和-drawLayer:inContext:方法的调用。通过drawRect绘制视图;
绘制string(字符串)

  • (void)drawRect:(CGRect)rect{
    NSLog(@"drawRect");
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(10, 10)];
    [path addLineToPoint:CGPointMake(20, 20)];
    [path closePath];
    path.lineWidth = 1;
    [[UIColor redColor] setStroke];
    [path stroke];
    }
    3> 准备提交(Prepare) - Image decoding, Image conversion(如果图片类型不是GPU所支持的,需要对图片进行转换)。
    4> 提交 - Core Animation打包所有的图层和动画,然后通过IPC(进程内通信)发送到渲染服务(render server,一个单独管理动画和图层组合的一个系统进程)。这个步骤是递归的,所以如果layer tree如果比较复杂此步骤代价比较高。

上面4个步骤发生在自己的应用程序内部,动画显示到屏幕之前还有2个步骤的工作:
5> 对所有图层属性计算中间值,设置OpenGL几何形状来执行渲染。
6> 在屏幕上渲染可见的三角形。

前5个阶段都在软件层面处理(通过CPU),只有最后一个阶段被GPU执行。6个阶段中只有布局和显示两个阶段是可以被我们控制的,Core Animation框架处理剩下的事务。

你可能感兴趣的:(第三方框架源码解析)