写在前面给大家推荐一个不错的站点 点击打开链接

本文測试样例源代码下载地址

近期看AFNetworking2的源代码。学习这个知名网络框架的实现。顺便梳理写下文章。

AFNetworking的代码还在不断更新中,我看的是AFNetworking2.3.1。

本篇先看看AFURLConnectionOperation,AFURLConnectionOperation继承自NSOperation,是一个封装好的任务单元,在这里构建了NSURLConnection,作为NSURLConnection的delegate处理请求回调,做好状态切换,线程管理,能够说是AFNetworking最核心的类,以下分几部分说下看源代码时注意的点,最后放上代码的凝视。
0.Tricks
AFNetworking代码中有一些经常使用技巧,先说明一下。
A.clang warning
  1. #pragma clang diagnostic push
  2. #pragma clang diagnostic ignored "-Wgnu"
  3. //code
  4. #pragma clang diagnostic pop
表示在这个区间里忽略一些特定的clang的编译警告,由于AFNetworking作为一个库被其它项目引用,所以不能全局忽略clang的一些警告,仅仅能在有须要的时候局部这样做,作者喜欢用?

:符号,所以常常见忽略-Wgnu警告的写法。详见这里。

B.dispatch_once
为保证线程安全。全部单例都用dispatch_once生成。保证仅仅运行一次。这也是iOS开发经常使用的技巧。比如:
  1. static dispatch_queue_t url_request_operation_completion_queue() {
  2. static dispatch_queue_t af_url_request_operation_completion_queue;
  3. static dispatch_once_t onceToken;
  4. dispatch_once(&onceToken, ^{
  5. af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue",   DISPATCH_QUEUE_CONCURRENT );
  6. });
  7. return af_url_request_operation_completion_queue;
  8. }
C.weak & strong self
常看到一个 block 要使用 self。会处理成在外部声明一个 weak 变量指向 self。在 block 里又声明一个 strong 变量指向 weakSelf:
  1. __weak __typeof(self)weakSelf = self;
  2. self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
  3. __strong __typeof(weakSelf)strongSelf = weakSelf;
  4. }];
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 处于什么状态,都运行这个任务。
  1. NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
  2. [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  3. [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:。
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:.

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 后调用 [strongSelf setCompletionBlock:nil] 把 completionBlock 设成 nil。手动释放 self(NSOperation对象) 持有的 completionBlock 对象,打破循环引用。
能够理解成对外保证传进来的block一定会被释放。解决外部使用使非常easy出现的因对象关系复杂导致循环引用的问题,让使用者不知道循环引用这个概念都能正确使用。http://www.joblai.com/news/news-show-18.htm

B.dispatch_group
这里同意用户让全部 operation 的 completionBlock 在一个 group 里运行,但我没看出这样做的作用,若想组装一组请求(见以下的batchOfRequestOperations)也不须要再让completionBlock在group里运行。求解。

C.”The Deallocation Problem”
作者在凝视里说这里重写的setCompletionBlock方法攻克了”The Deallocation Problem”,实际上并没有。”The Deallocation Problem”简单来说就是不要让UIKit的东西在子线程释放。
这里假设传进来的block持有了外部的UIViewController或其它UIKit对象(以下临时称为A对象),而且在请求完毕之前其它全部对这个A对象的引用都已经释放了,那么这个completionBlock就是最后一个持有这个A对象的,这个block释放时A对象也会释放。这个block在什么线程释放。A对象就会在什么线程释放。我们看到block释放的地方是url_request_operation_completion_queue(),这是AFNetworking特意生成的子线程,所以按理说A对象是会在子线程释放的,会导致UIKit对象在子线程释放。会有问题。
但AFNetworking实际用起来却没问题,想了非常久不得其解,后来做了实验。发现iOS5以后苹果对UIKit对象的释放做了特殊处理。仅仅要发如今子线程释放这些对象,就自己主动转到主线程去释放,断点出来是由一个叫_objc_deallocOnMainThreadHelper 的方法做的。假设不是UIKit对象就不会跳到主线程释放。AFNetworking2.0仅仅支持iOS6+,所以没问题。
5.batchOfRequestOperations
这里额外提供了一个便捷接口,能够传入一组请求。在全部请求完毕后回调 complionBlock。在每个请求完毕时回调 progressBlock 通知外面有多少个请求已完毕。详情參见代码凝视,这里须要说明下 dispatch_group_enter 和dispatch_group_leave 的使用。这两个方法用于把一个异步任务增加 group 里。
一般我们要把一个任务增加一个group里是这样:
  1. dispatch_group_async(group, queue, ^{
  2. block();
  3. });
这个写法等价于
  1. dispatch_async(queue, ^{
  2. dispatch_group_enter(group);
  3. block()
  4. dispatch_group_leave(group);
  5. });
假设要把一个异步任务增加group,这样即可不通了:
  1. dispatch_group_async(group, queue, ^{
  2. [self performBlock:^(){
  3. block();
  4. }];
  5. //未运行到block() group任务就已经完毕了
  6. });
这时须要这样写:
  1. dispatch_group_enter(group);
  2. [self performBlock:^(){
  3. block();
  4. dispatch_group_leave(group);
  5. }];
异步任务回调后才算这个group任务完毕。对batchOfRequest的实现来说就是请求完毕并回调后。才算这个任务完毕。
事实上这跟retain/release差点儿相同,都是计数,dispatch_group_enter时任务数+1,dispatch_group_leave时任务数-1,任务数为0时运行dispatch_group_notify的内容。
相关代码:-batchOfRequestOperations:progressBlock:completionBlock:
6.其它
A.锁
AFURLConnectionOperation 有一把递归锁,在全部会訪问/改动成员变量的对外接口都加了锁,由于这些对外的接口用户是能够在随意线程调用的。对于訪问和改动成员变量的接口。必须用锁保证线程安全。
B.序列化
AFNetworking 的多数类都支持序列化。但实现的是 NSSecureCoding 的接口,而不是 NSCoding,差别在于解数据时要指定 Class。用 -decodeObjectOfClass:forKey: 方法取代了 -decodeObjectForKey: 。

这样做更安全,由于序列化后的数据有可能被篡改。若不指定 Class,-decode 出来的对象可能不是原来的对象,有潜在风险。另外,NSSecureCoding 是 iOS 6 以上才有的。

详见这里。

这里在序列化时保存了当前任务状态,接收的数据等,但回调block是保存不了的。须要在取出来发送时又一次设置。能够像以下这样持久化保存和取出任务:
  1. AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
  2. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:operation];
  3. AFHTTPRequestOperation *operationFromDB = [NSKeyedUnarchiver unarchiveObjectWithData:data];
  4. [operationFromDB start];
C.backgroundTask
这里提供了setShouldExecuteAsBackgroundTaskWithExpirationHandler 接口,决定APP进入后台后是否继续发送接收请求。并在后台执行时间超时后取消全部请求。

在 dealloc 里须要调用 [application endBackgroundTask:] ,告诉系统这个后台任务已经完毕,不然系统会一直让你的APP执行在后台。直到超时。

相关代码:-setShouldExecuteAsBackgroundTaskWithExpirationHandler:, -dealloc:

7.AFHTTPRequestOperation
AFHTTPRequestOperation 继承了 AFURLConnectionOperation,把它放一起说是由于它没做多少事情。主要多了responseSerializer,暂停下载断点续传。以及提供接口请求成功失败的回调接口 -setCompletionBlockWithSuccess:failure:。详见源代码凝视。

8.源代码凝视
  1. AFURLConnectionOperation.m
  1. AFHTTPRequestOperation.m
本篇我们继续来看看AFNetworking的下一个模块 — AFURLRequestSerialization。
AFURLRequestSerialization用于帮助构建NSURLRequest,主要做了两个事情:
1.构建普通请求:格式化请求參数。生成HTTP Header。
2.构建multipart请求。
分别看看它在这两点详细做了什么。怎么做的。
1.构建普通请求
A.格式化请求參数
一般我们请求都会按key=value的方式带上各种參数,GET方法參数直接加在URL上,POST方法放在body上。NSURLRequest没有封装好这个參数的解析,仅仅能我们自己拼好字符串。

AFNetworking提供了接口,让參数能够是NSDictionary, NSArray, NSSet这些类型。再由内部解析成字符串后赋给NSURLRequest。

转化过程大致是这种:
  1. @{
  2. @"name" : @"bang",
  3. @"phone": @{@"mobile": @"xx", @"home": @"xx"},
  4. @"families": @[@"father", @"mother"],
  5. @"nums": [NSSet setWithObjects:@"1", @"2", nil]
  6. }
  7. ->
  8. @[
  9. field: @"name", value: @"bang",
  10. field: @"phone[mobile]", value: @"xx",
  11. field: @"phone[home]", value: @"xx",
  12. field: @"families[]", value: @"father",
  13. field: @"families[]", value: @"mother",
  14. field: @"nums", value: @"1",
  15. field: @"nums", value: @"2",
  16. ]
  17. ->
  18. name=bang&phone[mobile]=xx&phone[home]=xx&families[]=father&families[]=mother&nums=1&num=2
第一部分是用户传进来的数据。支持包括NSArray,NSDictionary,NSSet这三种数据结构。
第二部分是转换成AFNetworking内自己的数据结构。每个key-value对都用一个对象AFQueryStringPair表示。作用是最后能够依据不同的字符串编码生成各自的key=value字符串。主要函数是AFQueryStringPairsFromKeyAndValue,详见源代码凝视。
第三部分是最后生成NSURLRequest可用的字符串数据,而且对參数进行url编码,在AFQueryStringFromParametersWithEncoding这个函数里。
最后在把数据赋给NSURLRequest时依据不同的HTTP方法分别处理,对于GET/HEAD/DELETE方法,把參数加到URL后面。对于其它如POST/PUT方法,把数据加到body上,并设好HTTP头,告诉服务端字符串的编码。
B.HTTP Header
AFNetworking帮你组装好了一些HTTP请求头。包含语言Accept-Language,依据 [NSLocale preferredLanguages] 方法读取本地语言。快速服务端自己能接受的语言。还有构建 User-Agent,以及提供Basic Auth 认证接口,帮你把usernamepassword做 base64 编码后放入 HTTP 请求头。

详见源代码凝视。

C.其它格式化方式
HTTP请求參数不一定是要key=value形式,能够是不论什么形式的数据。能够是json格式,苹果的plist格式,二进制protobuf格式等,AFNetworking提供了方法能够非常easy扩展支持这些格式。默认就实现了json和plist格式。详见源代码的类AFJSONRequestSerializer和AFPropertyListRequestSerializer。
2.构建multipart请求
构建Multipart请求是占篇幅非常大的一个功能。AFURLRequestSerialization里2/3的代码都是在做这个事。
A.Multipart协议介绍
Multipart是HTTP协议为web表单新增的上传文件的协议,协议文档是rfc1867,它基于HTTP的POST方法,数据相同是放在body上,跟普通POST方法的差别是数据不是key=value形式,key=value形式难以表示文件实体,为此Multipart协议加入了分隔符,有自己的格式结构。大致例如以下:
—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—。
B.实现
接下来说说如何构造Multipart里的数据,最简单的方式就是直接拼数据,要发送一个文件。就直接把文件全部内容读取出来。再按上述协议加上头部和分隔符。拼接好数据后扔给NSURLRequest的body就能够发送了,非常easy。但这样做是不可用的,由于文件可能非常大,这样拼数据把整个文件读进内存,非常可能把内存撑爆了。
另外一种方法是不把文件读出来。不在内存拼,而是新建一个暂时文件。在这个文件上拼接数据。再把文件地址扔给NSURLRequest的bodyStream,这样上传的时候是分片读取这个文件,不会撑爆内存,但这样每次上传都须要新建个暂时文件,对这个暂时文件的管理也挺麻烦的。
第三种方法是构建自己的数据结构,仅仅保存要上传的文件地址,边上传边拼数据,上传是分片的,拼数据也是分片的。拼到文件实体部分时直接从原来的文件分片读取。这方法没上述两种的问题,仅仅是实现起来也没上述两种简单,AFNetworking就是实现这第三种方法。并且还更进一步,除了文件,还能够加入多个其它不同类型的数据。包含NSData,和InputStream。
AFNetworking 里 multipart 请求的使用方式是这样:
  1. AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
  2. NSDictionary *parameters = @{@"foo": @"bar"};
  3. NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
  4. [manager POST:@"http://example.com/resources.json" parameters:parameters constructingBodyWithBlock:^(id formData) {
  5. [formData appendPartWithFileURL:filePath name:@"image" error:nil];
  6. } success:^(AFHTTPRequestOperation *operation, id responseObject) {
  7. NSLog(@"Success: %@", responseObject);
  8. } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  9. NSLog(@"Error: %@", error);
  10. }];
这里通过constructingBodyWithBlock向使用者提供了一个AFStreamingMultipartFormData对象,调这个对象的几种append方法就能够加入不同类型的数据,包含FileURL/NSData/NSInputStream,AFStreamingMultipartFormData内部把这些append的数据转成不同类型的 AFHTTPBodyPart,加入到自己定义的 AFMultipartBodyStream 里。

最后把 AFMultipartBodyStream 赋给原来 NSMutableURLRequest的bodyStream。NSURLConnection 发送请求时会读取这个 bodyStream,在读取数据时会调用这个 bodyStream 的 -read:maxLength: 方法。AFMultipartBodyStream 重写了这种方法,不断读取之前 append进来的 AFHTTPBodyPart 数据直到读完。

AFHTTPBodyPart 封装了各部分数据的组装和读取,一个 AFHTTPBodyPart 就是一个数据块。实际上三种类型 (FileURL/NSData/NSInputStream) 的数据在 AFHTTPBodyPart 都转成 NSInputStream,读取数据时仅仅需读这个 inputStream。inputStream 仅仅保存了数据的实体,没有包含分隔符和头部,AFHTTPBodyPart 是边读取变拼接数据,用一个状态机确定如今数据读取到哪一部份。以及保存这个状态下已被读取的字节数,以此定位要读的数据位置,详见 AFHTTPBodyPart 的-read:maxLength:方法。
AFMultipartBodyStream封装了整个multipart数据的读取,主要是依据读取的位置确定如今要读哪一个AFHTTPBodyPart。AFStreamingMultipartFormData对外提供友好的append接口。并把构造好的AFMultipartBodyStream赋回给NSMutableURLRequest。关系大致例如以下图:
C.NSInputStream子类
NSURLRequest 的 setHTTPBodyStream 接受的是一个 NSInputStream* 參数,那我们要自己定义inputStream的话,创建一个 NSInputStream 的子类传给它是不是就能够了?实际上不行。这样做后用NSURLRequest 发出请求会导致 crash,提示 [xx _scheduleInCFRunLoop:forMode:]: unrecognized selector。
这是由于NSURLRequest实际上接受的不是 NSInputStream 对象,而是 CoreFoundation 的 CFReadStreamRef 对象,由于 CFReadStreamRef 和 NSInputStream 是 toll-free bridged,能够自由转换,但CFReadStreamRef 会用到 CFStreamScheduleWithRunLoop 这种方法,当它调用到这种方法时,object-c 的 toll-free bridging 机制会调用 object-c 对象 NSInputStream 的对应函数。这里就调用到了_scheduleInCFRunLoop:forMode:,若不实现这种方法就会crash。

详见这篇文章。

3.源代码凝视
  1. AFURLRequestSerialization.m

具体内容分析点击打开链接

AFNetworking2.0源代码解析相关推荐

  1. mysql8.0源代码解析_源码解读:MySQL 8.0 InnoDB无锁化设计的日志系统

    原标题:源码解读:MySQL 8.0 InnoDB无锁化设计的日志系统 作者介绍 张永翔,现任网易云RDS开发,持续关注MySQL及数据库运维领域,擅长MySQL运维,知乎ID:雁南归. MySQL ...

  2. mysql8.0源代码解析_MySQL8.0.11源码分析之mysql关键函数和执行流程

    mysql是命令行客户端程序 ,交互式输入SQL语句或从文件以批处理模式执行它们的命令行工具. 入口函数 int main(int argc, char *argv[]) { if (get_opti ...

  3. AFNetworking2.4.1解析

    1.官网文档外加点中文注释 AFNetworking官网(点击进入) AFNetworking翻译注释 Architecture(结构) NSURLConnection AFURLConnection ...

  4. 为什么 SAP Spartacus 4.0 源代码版本读取不到 CX_BASE_URL 环境变量的值?

    SAP Spartacus 4.0 源代码版本: 如下图所示,遇到了 404 错误: 错误的 url:可以看到端口号变成了 4200: http://localhost:4200/occ/v2/ele ...

  5. vue 1.0源代码重点难点分析

    本文分析vue1.0源代码从入口开始到编译输出到网页显示生效的整个过程,并重点分析一些vue源代码设计原理. vue初始化根组件的入口代码: 对于没有路由的单页应用来说,入口就是new Vue(opt ...

  6. overlayfs源代码解析

    overlayfs源代码解析 mount挂载 注册文件系统 挂载文件系统 读写目录 openat系统调用打开目录 getdents系统调用,搜索目录 创建/删除文件 创建文件 删除文件 读写文件 读/ ...

  7. 历经一个月,终于搞定了SVM(支持向量机)-附源代码解析

    历经一个月,终于搞定了SVM(支持向量机)-附源代码解析 前言 其实整体算下来,断断续续的也得有快两个月了(原谅博主比较笨).中间也有好几次放弃,不想写这篇总结了,但是之前立下的誓言,要将学习到的每一 ...

  8. Mozilla FireFox Gecko内核源代码解析(4.nsHTMLTokens)

    之前我们分析了nsHTMLTokenizer(详见其解析篇),其中我们了解到了,其中设计了如何配合 nsScanner对输入流循环地解析流程,如怎么进行回溯等流式操作.实际上其中并没有包含具体的字符比 ...

  9. AnnaKournikova病毒源代码解析

    AnnaKournikova病毒源代码解析 AnnaKournikova病毒源代码解析 本帖版权归原作者,其它网站转载须注明出处,传统媒体转载须事先与原作者和e龙西祠胡同[http://www.xic ...

最新文章

  1. WPF以Clickonce方式发布后使用管理员身份运行
  2. 转载:opencv错误rect错误
  3. Qt工作笔记-QML自定义圆形进度条(C++后端处理数据)
  4. linux下面java配置,linux下面配置java环境变量
  5. java分页封装到dao层,Node Dao层的封装与分页
  6. PCB板设计后期处理流程及工作步骤有哪些?2021-07-29
  7. HTML5期末大作业:美食坊网站设计——美食坊美食购物主题(15页) HTML+CSS+JavaScript
  8. QEMU虚拟机源码分析概论
  9. 计算机桌面图标怎样变大变小,win7电脑桌面图标变大怎么恢复?win7桌面图标怎么变小?...
  10. GCN的Python实现与源码分析
  11. 使用Cesium创建3dtiles管线(视频)
  12. 大多数人都自我嫌弃而不自知
  13. win10右键文件夹无反应
  14. 到西藏工作,出差,旅行,出游,体验,好奇一切的一切,大全科!!绝对是经典“教科书”类,上面见不到的!全面了解,西藏的生活水平,和注意事项,是去西藏,拉萨前的必读性文章。
  15. 基于几何法的机器人逆运动学求解--工业机器人前三个关节
  16. 解密最接近人脑的智能学习机器——深度学习及并行化实现
  17. Visual C++ Tips: warning C4996: 'stricmp' was declared deprecated
  18. 机器学习——核函数kernal
  19. 北航网络空间安全复习资料
  20. 《那些年,我们一起追的女孩》:他们台湾那些年

热门文章

  1. python colorbar位置大小调整_python - matplotlib相邻子图:添加colorbar更改子图的大小 - 堆栈内存溢出...
  2. Java 线程实时读取日志文件
  3. awk 系列Part7:awk 怎么从标准输入(STDIN)读取输入
  4. 《树莓派实战秘籍》——1.20 技巧20使用Swap添加额外的内存
  5. iOS 10 UserNotifications 框架解析
  6. HBase之四--(1):Java操作Hbase进行建表、删表以及对数据进行增删改查,条件查询...
  7. Dijkstra算法求单源最短路径
  8. SPOJ OTOCI 动态树 LCT
  9. 洛谷 P1890 gcd区间
  10. PHP中提问频率最高的11个面试题和答案