AFNetworking2系列的版本阅读

384 阅读4分钟

AFNetworking2.6.3系列源码阅读

背景:关于NSURLConnection的发送一个网络请求时候。若在子线程调用同步接口,一条线程只能处理一个请求,因为请求一发出去线程就阻塞住等待回调,需要给每个请求新建一个线程,这是很浪费的,这种方式唯一的好处应该是易于控制请求并发的数量。但是我每次创建一个线程也是浪费线程资源,所以AFNetworking采用了一个常驻线程:

进入正题

1、AFNetworking,进入AFNetworking文件,找到AFURLConnectionOperation去seeing。

问题解答:AFNetworking是怎么解决常驻线程的了,其实很简单,用到了一个[NSMachPort port],我们知道应用在启动的时候有一个mainRunLoop的东西,这个就是一个while循环,其实底层源码实现就能看出。mainRunLoop为啥自己写的会占用内存比较好,而且CPU一直在执行了。其实自己也可以实现这种在某些特定情况下不占有CPU和内存的,使用睡眠,我在监听的port没有任务时候我就sleep,这不就简单的实现了吗。又说多了,哈哈,继续。我们在添加了一个[NSMachPort port]的东西后,而且[NSRunLoop currentRunLoop]在子线程中获取currentRunLoop后,就相当于当前的线程一直在监听这个currentRunLoop有没有任务,它怎么可能会执行完毕任务了。这就是AFNetworking简单实现常驻线程的原理。下面是代码

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });

    return _networkRequestThread;
}

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];

        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

2、看到这儿都不用多想了,自己也可以。实现自己的NSRunLoop和常驻线程了,原来这么简单,其实不是。简单谈谈NSMachPort,其实端口通信只用于底层的通信,最早是服务器之间的通信,为啥了,我写一个命令或者其他过去不久可以了吗,这时候你想想,如果没有统一的格式限制,那么写出来的端口通信就千奇百怪,其实好处不止这点,就说这儿吧,继续下面的。

3、看看初始化方法

- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
    NSParameterAssert(urlRequest);
    ......
    }

4、NSParameterAssert这是什么鬼,其实了很简单,尤其是写工具的人一般都会用单,这是断言NSParameterAssert,可以理解为我肯定urlRequest有值,不然会阻止在这儿。

5、NSRecursiveLock 在初始化的时候创建一把递归锁,为啥了,因为人家考虑到在调用这个的时候可能递归使用,防止死锁。递归锁不清楚的可以看看八大锁。

6、下面看一行代码,这个只要是为了解决其他runLoopModes的模式的时候,网络请求这是事件类型不能回调,所以就采用一个NSRunLoopCommonModes类型,以为NSRunLoop在循环过程中只有一个类型的model :

self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];

7、接下来看看重心位置,start函数,先贴代码:

方法1:
- (void)start {
    [self.lock lock];
    if ([self isCancelled]) {
        [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    } else if ([self isReady]) {
        self.state = AFOperationExecutingState;

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

==========================================

方法2:
- (void)operationDidStart {
    [self.lock lock];
    if (![self isCancelled]) {
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        for (NSString *runLoopMode in self.runLoopModes) {
            [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
            [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
        }

        [self.outputStream open];
        [self.connection start];
    }
    [self.lock unlock];

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

==========================================

方法3:
- (void)cancelConnection {
    NSDictionary *userInfo = nil;
    if ([self.request URL]) {
        userInfo = @{NSURLErrorFailingURLErrorKey : [self.request URL]};
    }
    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 {
            // Accommodate race condition where `self.connection` has not yet been set before cancellation
            self.error = error;
            [self finish];
        }
    }
}



7、1 AFURLConnectionOperation 类继承 NSOperation ,NSOperation是一个抽象类,也是一个实现线程的抽象类。但是里面实现了start方法,可以看出里面实现了这些一部的方法,而且这个AFURLConnectionOperation是自定义这个任务结束的,因为它实现了start方法。

- (void)start {
    ......
}

==========================================

- (void)finish {
    ......
}

7、2 NSOperation是一个抽象类,然而start方法就算执行完毕,它的finish属性也不会变,所以要在执行完的地方自己调用finish方法,因此你可以控制这个operation的生命周期,可以cancle,或者其他操作。但是main的话,如果任务添加大queue中执行完毕后就会将任务移除,如果外部需要异步执行其他任务就不可以了。

7、3 在start函数中 performSelector 执行了在指定线程中执行 operationDidStart ,在 operationDidStart 中又调用了 lock ,递归锁的强大之处。,然后才真正的调用了发起网络请求者 NSURLConnection 的 initWithRequest ,后面的startImmediately表明非立刻请求。

7、4 scheduleInRunLoop是将self.connection该对象配置到一个runloop接收stream events,说明我当前的这种事件就像stream events没有结束状态,关于stream events有疑问,可以看下事件的分类,现在AFNet3.0采用的session事件方式。

7、5 看到这而不说 AFNetworking 当初设计者的那种把每个类都用的是极点。

7、6 后续其实内容的话见下一个版本。