谈谈 MVX 中的 Controller
+在前两篇文章中,我们已经对 iOS 中的 Model 层以及 View 层进行了分析,划分出了它们的具体职责,其中 Model 层除了负责数据的持久存储、缓存工作,还要负责所有 HTTP... »
+diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 652d89e..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.md linguist-language=Objective-C - diff --git a/.github/ISSUE_REPLY_TEMPLATE.md b/.github/ISSUE_REPLY_TEMPLATE.md new file mode 100644 index 0000000..944c73f --- /dev/null +++ b/.github/ISSUE_REPLY_TEMPLATE.md @@ -0,0 +1,3 @@ +# 注意 + +由于评论维护的问题,所有在 GitHub Issue 中提的问题都不会得到作者的回复,请到对应[博客](http://draveness.me)下面的 Disqus 评论系统留言,谢谢。 diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 0000000..944c73f --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,3 @@ +# 注意 + +由于评论维护的问题,所有在 GitHub Issue 中提的问题都不会得到作者的回复,请到对应[博客](http://draveness.me)下面的 Disqus 评论系统留言,谢谢。 diff --git a/.gitignore b/.gitignore index 8b8e4f4..cfc4d70 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ fastlane/report.xml # Pods/ +.DS_Store diff --git a/README.md b/README.md index cf6c9a9..c4ee9dd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# iOS-Source-Code-Analyze +# Analyze
@@ -7,32 +7,45 @@
## 为什么要建这个仓库
+欢迎使用 RSS 订阅我的博客 [点击订阅](http://draveness.me/feed.xml)
+
世人都说阅读开源框架的源代码对于功力有显著的提升,所以我也尝试阅读开源框架的源代码,并对其内容进行详细地分析和理解。在这里将自己阅读开源框架源代码的心得记录下来,希望能对各位开发者有所帮助。我会不断更新这个仓库中的文章,如果想要关注可以点 `star`。
## 目录
-Latest:[预加载与智能预加载(iOS)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AsyncDisplayKit/预加载与智能预加载(iOS).md)
+Latest:
+
++ [谈谈 MVX 中的 Model](contents/architecture/mvx-model.md)
++ [谈谈 MVX 中的 View](contents/architecture/mvx-view.md)
++ [谈谈 MVX 中的 Controller](contents/architecture/mvx-controller.md)
++ [浅谈 MVC、MVP 和 MVVM 架构模式](contents/architecture/mvx.md)
| Project | Version | Article |
|:-------:|:-------:|:------|
-| AsyncDisplayKit | 1.9.81 | [提升 iOS 界面的渲染性能](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AsyncDisplayKit/提升%20iOS%20界面的渲染性能%20.md)
[从 Auto Layout 的布局算法谈性能](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AsyncDisplayKit/从%20Auto%20Layout%20的布局算法谈性能.md)
[预加载与智能预加载(iOS)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AsyncDisplayKit/预加载与智能预加载(iOS).md)|
-| CocoaPods | 1.1.0 | [CocoaPods 都做了什么?](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/CocoaPods/CocoaPods%20都做了什么?.md)
[谈谈 DSL 以及 DSL 的应用(以 CocoaPods 为例)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/CocoaPods/谈谈%20DSL%20以及%20DSL%20的应用(以%20CocoaPods%20为例).md)|
-| OHHTTPStubs | 5.1.0 | [iOS 开发中使用 NSURLProtocol 拦截 HTTP 请求](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/OHHTTPStubs/iOS%20开发中使用%20NSURLProtocol%20拦截%20HTTP%20请求.md)
[如何进行 HTTP Mock(iOS)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/OHHTTPStubs/如何进行%20HTTP%20Mock(iOS).md) |
-| ProtocolKit | | [如何在 Objective-C 中实现协议扩展](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/ProtocolKit/如何在%20Objective-C%20中实现协议扩展.md) |
-| FBRetainCycleDetector | 0.1.2 | [如何在 iOS 中解决循环引用的问题](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/FBRetainCycleDetector/如何在%20iOS%20中解决循环引用的问题.md)
[检测 NSObject 对象持有的强指针](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/FBRetainCycleDetector/检测%20NSObject%20对象持有的强指针.md)
[如何实现 iOS 中的 Associated Object](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/FBRetainCycleDetector/如何实现%20iOS%20中的%20Associated%20Object.md)
[iOS 中的 block 是如何持有对象的](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/FBRetainCycleDetector/iOS%20中的%20block%20是如何持有对象的.md)|
-| fishhook | 0.2 |[动态修改 C 语言函数的实现](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/fishhook/动态修改%20C%20语言函数的实现.md) |
-| libextobjc | |[如何在 Objective-C 的环境下实现 defer](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/libextobjc/如何在%20Objective-C%20的环境下实现%20defer.md) |
-| IQKeyboardManager | 4.0.3 |[『零行代码』解决键盘遮挡问题(iOS)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/IQKeyboardManager/『零行代码』解决键盘遮挡问题(iOS).md) |
-| ObjC | | [从 NSObject 的初始化了解 isa](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/从%20NSObject%20的初始化了解%20isa.md)
[深入解析 ObjC 中方法的结构](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/深入解析%20ObjC%20中方法的结构.md)
[从源代码看 ObjC 中消息的发送](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/从源代码看%20ObjC%20中消息的发送.md)
[你真的了解 load 方法么?](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/你真的了解%20load%20方法么?.md)
[上古时代 Objective-C 中哈希表的实现](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/上古时代%20Objective-C%20中哈希表的实现.md)
[自动释放池的前世今生](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/自动释放池的前世今生.md)
[黑箱中的 retain 和 release](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/黑箱中的%20retain%20和%20release.md)
[关联对象 AssociatedObject 完全解析](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/关联对象%20AssociatedObject%20完全解析.md)
[懒惰的 initialize 方法](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/懒惰的%20initialize%20方法.md)
[对象是如何初始化的(iOS)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/对象是如何初始化的(iOS).md)|
-| DKNightVersion | 2.3.0 | [成熟的夜间模式解决方案](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/DKNightVersion/成熟的夜间模式解决方案.md) |
-| AFNetworking | 3.0.4 | [AFNetworking 概述(一)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/AFNetworking%20概述(一).md)
[AFNetworking 的核心 AFURLSessionManager(二)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/AFNetworking%20的核心%20AFURLSessionManager(二).md)
[处理请求和响应 AFURLSerialization(三)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/处理请求和响应%20AFURLSerialization(三).md)
[AFNetworkReachabilityManager 监控网络状态(四)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/AFNetworkReachabilityManager%20监控网络状态(四).md)
[验证 HTTPS 请求的证书(五)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/验证%20HTTPS%20请求的证书(五).md) |
-| BlocksKit | 2.2.5 | [神奇的 BlocksKit(一)遍历、KVO 和分类](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/BlocksKit/神奇的%20BlocksKit%20(一).md)
[神奇的 BlocksKit(二)动态代理的实现 ](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/BlocksKit/神奇的%20BlocksKit%20(二).md) |
-| Alamofire | | [iOS 源代码分析 --- Alamofire](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/Alamofire/iOS%20源代码分析%20----%20Alamofire.md) |
-| SDWebImage | | [iOS 源代码分析 --- SDWebImage](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/SDWebImage/iOS%20源代码分析%20---%20SDWebImage.md) |
-| MBProgressHUD | | [iOS 源代码分析 --- MBProgressHUD](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/MBProgressHUD/iOS%20源代码分析%20---%20MBProgressHUD.md) |
-| Masonry | | [iOS 源代码分析 --- Masonry](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/Masonry/iOS%20源代码分析%20---%20Masonry.md) |
-| Redis | 3.2.5 | [Redis 和 I/O 多路复用](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/Redis/redis-io-multiplexing.md)
[Redis 中的事件循环](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/Redis/redis-eventloop.md)
[Redis 是如何处理命令的(客户端)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/Redis/redis-cli)|
-
+| Architecture | | [谈谈 MVX 中的 Model](contents/architecture/mvx-model.md)
[谈谈 MVX 中的 View](contents/architecture/mvx-view.md)
[谈谈 MVX 中的 Controller](contents/architecture/mvx-controller.md)
[浅谈 MVC、MVP 和 MVVM 架构模式](contents/architecture/mvx.md) |
+| ReactiveObjC | 2.1.2 | [『状态』驱动的世界:ReactiveCocoa](contents/ReactiveObjC/RACSignal.md)
[Pull-Driven 的数据流 RACSequence](contents/ReactiveObjC/RACSequence.md)
[『可变』的热信号 RACSubject](contents/ReactiveObjC/RACSubject.md)
[优雅的 RACCommand](contents/ReactiveObjC/RACCommand.md)
[用于多播的 RACMulticastConnection](contents/ReactiveObjC/RACMulticastConnection.md)
[RAC 中的双向数据绑定 RACChannel](contents/ReactiveObjC/RACChannel.md)
[理解 RACScheduler 的实现](contents/ReactiveObjC/RACScheduler.md)
[从代理到 RACSignal](contents/ReactiveObjC/RACDelegateProxy.md)|
+| ObjC | | [从 NSObject 的初始化了解 isa](contents/objc/从%20NSObject%20的初始化了解%20isa.md)
[深入解析 ObjC 中方法的结构](contents/objc/深入解析%20ObjC%20中方法的结构.md)
[从源代码看 ObjC 中消息的发送](contents/objc/从源代码看%20ObjC%20中消息的发送.md)
[你真的了解 load 方法么?](contents/objc/你真的了解%20load%20方法么?.md)
[上古时代 Objective-C 中哈希表的实现](contents/objc/上古时代%20Objective-C%20中哈希表的实现.md)
[自动释放池的前世今生](contents/objc/自动释放池的前世今生.md)
[黑箱中的 retain 和 release](contents/objc/黑箱中的%20retain%20和%20release.md)
[关联对象 AssociatedObject 完全解析](contents/objc/关联对象%20AssociatedObject%20完全解析.md)
[懒惰的 initialize 方法](contents/objc/懒惰的%20initialize%20方法.md)
[对象是如何初始化的(iOS)](contents/objc/对象是如何初始化的(iOS).md)|
+| KVOController | 1.2.0 | [如何优雅地使用 KVO](contents/KVOController/KVOController.md) |
+| AsyncDisplayKit | 1.9.81 | [提升 iOS 界面的渲染性能](contents/AsyncDisplayKit/提升%20iOS%20界面的渲染性能%20.md)
[从 Auto Layout 的布局算法谈性能](contents/AsyncDisplayKit/从%20Auto%20Layout%20的布局算法谈性能.md)
[预加载与智能预加载(iOS)](contents/AsyncDisplayKit/预加载与智能预加载(iOS).md)|
+| CocoaPods | 1.1.0 | [CocoaPods 都做了什么?](contents/CocoaPods/CocoaPods%20都做了什么?.md)
[谈谈 DSL 以及 DSL 的应用(以 CocoaPods 为例)](contents/CocoaPods/谈谈%20DSL%20以及%20DSL%20的应用(以%20CocoaPods%20为例).md)|
+| OHHTTPStubs | 5.1.0 | [iOS 开发中使用 NSURLProtocol 拦截 HTTP 请求](contents/OHHTTPStubs/iOS%20开发中使用%20NSURLProtocol%20拦截%20HTTP%20请求.md)
[如何进行 HTTP Mock(iOS)](contents/OHHTTPStubs/如何进行%20HTTP%20Mock(iOS).md) |
+| ProtocolKit | | [如何在 Objective-C 中实现协议扩展](contents/ProtocolKit/如何在%20Objective-C%20中实现协议扩展.md) |
+| FBRetainCycleDetector | 0.1.2 | [如何在 iOS 中解决循环引用的问题](contents/FBRetainCycleDetector/如何在%20iOS%20中解决循环引用的问题.md)
[检测 NSObject 对象持有的强指针](contents/FBRetainCycleDetector/检测%20NSObject%20对象持有的强指针.md)
[如何实现 iOS 中的 Associated Object](contents/FBRetainCycleDetector/如何实现%20iOS%20中的%20Associated%20Object.md)
[iOS 中的 block 是如何持有对象的](contents/FBRetainCycleDetector/iOS%20中的%20block%20是如何持有对象的.md)|
+| fishhook | 0.2 |[动态修改 C 语言函数的实现](contents/fishhook/动态修改%20C%20语言函数的实现.md) |
+| libextobjc | |[如何在 Objective-C 的环境下实现 defer](contents/libextobjc/如何在%20Objective-C%20的环境下实现%20defer.md) |
+| IQKeyboardManager | 4.0.3 |[『零行代码』解决键盘遮挡问题(iOS)](contents/IQKeyboardManager/『零行代码』解决键盘遮挡问题(iOS).md) |
+| DKNightVersion | 2.3.0 | [成熟的夜间模式解决方案](contents/DKNightVersion/成熟的夜间模式解决方案.md) |
+| AFNetworking | 3.0.4 | [AFNetworking 概述(一)](contents/AFNetworking/AFNetworking%20概述(一).md)
[AFNetworking 的核心 AFURLSessionManager(二)](contents/AFNetworking/AFNetworking%20的核心%20AFURLSessionManager(二).md)
[处理请求和响应 AFURLSerialization(三)](contents/AFNetworking/处理请求和响应%20AFURLSerialization(三).md)
[AFNetworkReachabilityManager 监控网络状态(四)](contents/AFNetworking/AFNetworkReachabilityManager%20监控网络状态(四).md)
[验证 HTTPS 请求的证书(五)](contents/AFNetworking/验证%20HTTPS%20请求的证书(五).md) |
+| BlocksKit | 2.2.5 | [神奇的 BlocksKit(一)遍历、KVO 和分类](contents/BlocksKit/神奇的%20BlocksKit%20(一).md)
[神奇的 BlocksKit(二)动态代理的实现 ](contents/BlocksKit/神奇的%20BlocksKit%20(二).md) |
+| Alamofire | | [iOS 源代码分析 --- Alamofire](contents/Alamofire/iOS%20源代码分析%20----%20Alamofire.md) |
+| SDWebImage | | [iOS 源代码分析 --- SDWebImage](contents/SDWebImage/iOS%20源代码分析%20---%20SDWebImage.md) |
+| MBProgressHUD | | [iOS 源代码分析 --- MBProgressHUD](contents/MBProgressHUD/iOS%20源代码分析%20---%20MBProgressHUD.md) |
+| Masonry | | [iOS 源代码分析 --- Masonry](contents/Masonry/iOS%20源代码分析%20---%20Masonry.md) |
+| Redis | 3.2.5 | [Redis 和 I/O 多路复用](contents/Redis/redis-io-multiplexing.md)
[Redis 中的事件循环](contents/Redis/redis-eventloop.md)
[Redis 是如何处理命令的(客户端)](contents/Redis/redis-cli)|
+
+## 微信公众号
+
+
## 勘误
diff --git "a/contents/AFNetworking/AFNetworking \347\232\204\346\240\270\345\277\203 AFURLSessionManager\357\274\210\344\272\214\357\274\211.md" "b/contents/AFNetworking/AFNetworking \347\232\204\346\240\270\345\277\203 AFURLSessionManager\357\274\210\344\272\214\357\274\211.md"
index 530afb3..bb4e823 100644
--- "a/contents/AFNetworking/AFNetworking \347\232\204\346\240\270\345\277\203 AFURLSessionManager\357\274\210\344\272\214\357\274\211.md"
+++ "b/contents/AFNetworking/AFNetworking \347\232\204\346\240\270\345\277\203 AFURLSessionManager\357\274\210\344\272\214\357\274\211.md"
@@ -113,7 +113,7 @@ Blog: [Draveness](http://draveness.me)
> `url_session_manager_create_task_safely` 的调用是因为苹果框架中的一个 bug [#2093](https://github.com/AFNetworking/AFNetworking/issues/2093),如果有兴趣可以看一下,在这里就不说明了
1. 调用 `- [NSURLSession dataTaskWithRequest:]` 方法传入 `NSURLRequest`
-2. 调用 `- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:]` 方法返回一个 `AFURLSessionManagerTaskDelegate` 对象
+2. 调用 `- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:]` 方法创建一个 `AFURLSessionManagerTaskDelegate` 对象
3. 将 `completionHandler` `uploadProgressBlock` 和 `downloadProgressBlock` 传入该对象并在相应事件发生时进行回调
```objectivec
diff --git "a/contents/AsyncDisplayKit/\344\273\216 Auto Layout \347\232\204\345\270\203\345\261\200\347\256\227\346\263\225\350\260\210\346\200\247\350\203\275.md" "b/contents/AsyncDisplayKit/\344\273\216 Auto Layout \347\232\204\345\270\203\345\261\200\347\256\227\346\263\225\350\260\210\346\200\247\350\203\275.md"
index 3db04db..aff99d4 100644
--- "a/contents/AsyncDisplayKit/\344\273\216 Auto Layout \347\232\204\345\270\203\345\261\200\347\256\227\346\263\225\350\260\210\346\200\247\350\203\275.md"
+++ "b/contents/AsyncDisplayKit/\344\273\216 Auto Layout \347\232\204\345\270\203\345\261\200\347\256\227\346\263\225\350\260\210\346\200\247\350\203\275.md"
@@ -302,7 +302,7 @@ ASDK 的文档中推荐在子类中覆写 `- layoutSpecThatFits:` 方法,返
## References
-+ [Cassowary, Cocoa Auto Layout, and enaml constraints](http://stacks.11craft.com/cassowary-cocoa-Auto Layout-and-enaml-constraints.html)
++ [Cassowary, Cocoa Auto Layout, and enaml constraints](http://stacks.11craft.com/cassowary-cocoa-autolayout-and-enaml-constraints.html)
+ [Solving constraint systems](http://cassowary.readthedocs.io/en/latest/topics/theory.html)
+ [Auto Layout Performance on iOS](http://floriankugler.com/2013/04/22/auto-layout-performance-on-ios/)
+ [The Cassowary Linear Arithmetic Constraint Solving Algorithm: Interface and Implementation](https://constraints.cs.washington.edu/cassowary/cassowary-tr.pdf)
diff --git "a/contents/BlocksKit/\347\245\236\345\245\207\347\232\204 BlocksKit \357\274\210\344\270\200\357\274\211.md" "b/contents/BlocksKit/\347\245\236\345\245\207\347\232\204 BlocksKit \357\274\210\344\270\200\357\274\211.md"
index 68bd9f9..b718ad1 100644
--- "a/contents/BlocksKit/\347\245\236\345\245\207\347\232\204 BlocksKit \357\274\210\344\270\200\357\274\211.md"
+++ "b/contents/BlocksKit/\347\245\236\345\245\207\347\232\204 BlocksKit \357\274\210\344\270\200\357\274\211.md"
@@ -94,7 +94,7 @@ BlocksKit 在这些集合类中所添加的一些方法在 Ruby、Haskell 等语
```objectivec
NSObject *test = [[NSObject alloc] init];
-[test bk_associateValue:@"Draveness" withKey:@" name"];
+[test bk_associateValue:@"Draveness" withKey:@"name"];
NSLog(@"%@",[test bk_associatedValueForKey:@"name"]);
2016-03-05 16:02:25.761 Draveness[10699:452125] Draveness
diff --git a/contents/Blog/images/initialize-comments/new-token.png b/contents/Blog/images/initialize-comments/new-token.png
new file mode 100644
index 0000000..9404350
Binary files /dev/null and b/contents/Blog/images/initialize-comments/new-token.png differ
diff --git a/contents/Blog/images/initialize-comments/personal-access-token.png b/contents/Blog/images/initialize-comments/personal-access-token.png
new file mode 100644
index 0000000..bd43b0a
Binary files /dev/null and b/contents/Blog/images/initialize-comments/personal-access-token.png differ
diff --git a/contents/Blog/initialize-comments.md b/contents/Blog/initialize-comments.md
new file mode 100644
index 0000000..ded378d
--- /dev/null
+++ b/contents/Blog/initialize-comments.md
@@ -0,0 +1,136 @@
+# 如何自动初始化 Gitalk/Gitment 评论
+
+之前的博客一直都使用 Disqus 作为评论系统,然后因为 Disqus 在国内无法访问,很多读者都只能通过邮件的方式咨询一些问题,昨天觉得长痛不如短痛,直接将博客的评论迁移到了 [Gitalk](https://github.com/gitalk/gitalk),最开始选择了使用 Gitment 作为评论系统,但是由于其开发者很久没有维护、代码七个月也没有更新,所以就选择了有更多人维护的 Gitalk 作为目前博客的评论系统。
+
+无论是 Gitalk 还是 Gitment 都只能手动初始化所有文章的评论或者一个一个点开界面,作者觉得这件事情非常麻烦,所以手动抓了一下 Gitalk 和 Gitment 在初始化评论时发出的网络请求后写了一个用于自动化初始评论的脚本。
+
+## 获得权限
+
+在使用该脚本之前首先要在 GitHub 创建一个新的 [Personal access tokens](https://github.com/settings/tokens),选择 `Generate new token` 后,在当前的页面中为 Token 添加所有 Repo 的权限:
+
+
+
+在这里创建之后,点击界面最下的面 `Generate token` 按钮获得一个新的 token:
+
+
+
+> 作者已经把这个 token 删掉了,不要想着用这个 token 就能获得到作者 GitHub 的权限。
+
+## 脚本
+
+作者在抓取了 Gitalk 和 Gitment 的 API 请求发现,这两个评论服务是**通过 GitHub 提供的 API 创建含有相应标签的 issue**,所以我们应该也可以直接使用 GitHub 的 API 创建所有博客文章对应的 issue,这与通过评论插件创建 issue 是完全一样的,在创建之后无论是 Gitalk 还是 Gitment 都可以通过对应的标签直接在仓库中找到对应的 issue 了。
+
+本文中提供的自动化脚本使用的是 Ruby 代码,请确定自己的机器上已经安装了 Ruby,并且使用如下的命令安装脚本所需要的所有依赖:
+
+```ruby
+$ sudo gem install faraday activesupport sitemap-parser
+```
+
+### 使用 sitemap 文件
+
+如果我们使用的博客服务是 Jekyll,那么就可以通过 [jekyll-sitemap](https://github.com/jekyll/jekyll-sitemap) 插件为博客创建对应的 sitemap 文件,例如:https://draveness.me/sitemap.xml。
+
+有 sitemap 文件之后就非常好办了,在任意目录下创建 `comment.rb` 文件后,将下面的代码粘贴到文件中:
+
+```ruby
+username = "draveness" # GitHub 用户名
+new_token = "xxxxxxx" # GitHub Token
+repo_name = "github.amrom.workers.devments-repo" # 存放 issues
+sitemap_url = "https://draveness.me/sitemap.xml" # sitemap
+kind = "Gitalk" # "Gitalk" or "gitment"
+
+require 'open-uri'
+require 'faraday'
+require 'active_support'
+require 'active_support/core_ext'
+require 'sitemap-parser'
+
+sitemap = SitemapParser.new sitemap_url
+urls = sitemap.to_a
+
+conn = Faraday.new(:url => "https://api.github.com/repos/#{username}/#{repo_name}/issues") do |conn|
+ conn.basic_auth(username, token)
+ conn.adapter Faraday.default_adapter
+end
+
+urls.each_with_index do |url, index|
+ title = open(url).read.scan(/
在前两篇文章中,我们已经对 iOS 中的 Model 层以及 View 层进行了分析,划分出了它们的具体职责,其中 Model 层除了负责数据的持久存储、缓存工作,还要负责所有 HTTP... »
+ > 所有继承自 `NSObject` 的类实例化后的对象都会包含一个类型为 `isa_t` 的结构体。 @@ -42,12 +41,10 @@ struct objc_class : objc_object { > 关于如何在 `class_data_bits_t` 中查找对应方法会在之后的文章中讲到。这里只需要知道,它会在这个结构体中查找到对应方法的实现就可以了。[深入解析 ObjC 中方法的结构](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/深入解析%20ObjC%20中方法的结构.md) -
 但是,这样就有一个问题,类方法的实现又是如何查找并且调用的呢?这时,就需要引入*元类*来保证无论是类还是对象都能**通过相同的机制查找方法的实现**。 -
 @@ -58,7 +55,6 @@ struct objc_class : objc_object { 下面这张图介绍了对象,类与元类之间的关系,笔者认为已经觉得足够清晰了,所以不在赘述。 -
 > 图片来自 [objc_explain_Classes_and_metaclasses](http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html) @@ -111,7 +107,6 @@ union isa_t { `isa_t` 是一个 `union` 类型的结构体,对 `union` 不熟悉的读者可以看这个 stackoverflow 上的[回答](http://stackoverflow.com/questions/252552/why-do-we-need-c-unions). 也就是说其中的 `isa_t`、`cls`、 `bits` 还有结构体共用同一块地址空间。而 `isa` 总共会占据 64 位的内存空间(决定于其中的结构体) -
 ```objectivec @@ -173,7 +168,6 @@ inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) 我们可以把它转换成二进制的数据,然后看一下哪些属性对应的位被这行代码初始化了(标记为红色): -
 从图中了解到,在使用 `ISA_MAGIC_VALUE` 设置 `isa_t` 结构体之后,实际上只是设置了 `indexed` 以及 `magic` 这两部分的值。 @@ -224,7 +218,6 @@ inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) isa.has_cxx_dtor = hasCxxDtor; ``` -
 ### `shiftcls` @@ -242,7 +235,6 @@ isa.shiftcls = (uintptr_t)cls >> 3; 而 ObjC 中的类指针的地址后三位也为 0,在 `_class_createInstanceFromZone` 方法中打印了调用这个方法传入的类指针: -
 可以看到,这里打印出来的**所有类指针十六进制地址的最后一位都为 8 或者 0**。也就是说,类指针的后三位都为 0,所以,我们在上面存储 `Class` 指针时右移三位是没有问题的。 @@ -253,7 +245,6 @@ isa.shiftcls = (uintptr_t)cls >> 3; 如果再尝试打印对象指针的话,会发现所有对象内存地址的**后四位**都是 0,说明 ObjC 在初始化内存时是以 16 个字节对齐的, 分配的内存地址后四位都是 0。 -
 > 使用整个指针大小的内存来存储 `isa` 指针有些浪费,尤其在 64 位的 CPU 上。在 `ARM64` 运行的 iOS 只使用了 33 位作为指针(与结构体中的 33 位无关,Mac OS 上为 47 位),而剩下的 31 位用于其它目的。类的指针也同样根据字节对齐了,每一个类指针的地址都能够被 8 整除,也就是使最后 3 bits 为 0,为 `isa` 留下 34 位用于性能的优化。 @@ -263,7 +254,6 @@ isa.shiftcls = (uintptr_t)cls >> 3; 我尝试运行了下面的代码将 `NSObject` 的类指针和对象的 `isa` 打印出来,具体分析一下 -
 ``` diff --git "a/contents/objc/\344\273\216\346\272\220\344\273\243\347\240\201\347\234\213 ObjC \344\270\255\346\266\210\346\201\257\347\232\204\345\217\221\351\200\201.md" "b/contents/objc/\344\273\216\346\272\220\344\273\243\347\240\201\347\234\213 ObjC \344\270\255\346\266\210\346\201\257\347\232\204\345\217\221\351\200\201.md" index 0803deb..24f8734 100644 --- "a/contents/objc/\344\273\216\346\272\220\344\273\243\347\240\201\347\234\213 ObjC \344\270\255\346\266\210\346\201\257\347\232\204\345\217\221\351\200\201.md" +++ "b/contents/objc/\344\273\216\346\272\220\344\273\243\347\240\201\347\234\213 ObjC \344\270\255\346\266\210\346\201\257\347\232\204\345\217\221\351\200\201.md" @@ -10,7 +10,7 @@ 2. `[receiver message]` 会被翻译为 `objc_msgSend(receiver, @selector(message))` 3. 在消息的响应链中**可能**会调用 `- resolveInstanceMethod:` `- forwardInvocation:` 等方法 4. 关于选择子 SEL 的知识 - + > 如果对于上述的知识不够了解,可以看一下这篇文章 [Objective-C Runtime](http://tech.glowing.com/cn/objective-c-runtime/),但是其中关于 `objc_class` 的结构体的代码已经过时了,不过不影响阅读以及理解。 5. 方法在内存中存储的位置,[深入解析 ObjC 中方法的结构](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/objc/深入解析%20ObjC%20中方法的结构.md)(可选) @@ -26,7 +26,6 @@ 由于这个系列的文章都是对 Objective-C 源代码的分析,所以会**从 Objective-C 源代码中分析并合理地推测一些关于消息传递的问题**。 -
 ## 关于 @selector() 你需要知道的 @@ -87,7 +86,6 @@ int main(int argc, const char * argv[]) { 在主函数任意位置打一个断点, 比如 `-> [object hello];` 这里,然后在 lldb 中输入: -
 这里面我们打印了两个选择子的地址` @selector(hello)` 以及 `@selector(undefined_hello_method)`,需要注意的是: @@ -96,7 +94,6 @@ int main(int argc, const char * argv[]) { 如果我们修改程序的代码: -
 在这里,由于我们在代码中显示地写出了 `@selector(undefined_hello_method)`,所以在 lldb 中再次打印这个 `sel` 内存地址跟之前相比有了很大的改变。 @@ -111,7 +108,6 @@ int main(int argc, const char * argv[]) { 在运行时初始化之前,打印 `hello` 选择子的的内存地址: -
 ## message.h 文件 @@ -119,10 +115,10 @@ int main(int argc, const char * argv[]) { Objective-C 中 `objc_msgSend` 的实现并没有开源,它只存在于 `message.h` 这个头文件中。 ```objectivec -/** +/** * @note When it encounters a method call, the compiler generates a call to one of the * functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret. - * Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper; + * Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper; * other messages are sent using \c objc_msgSend. Methods that have data structures as return values * are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret. */ @@ -177,7 +173,6 @@ int main(int argc, const char * argv[]) { 在调用 `hello` 方法的这一行打一个断点,当我们尝试进入(Step in)这个方法只会直接跳入这个方法的实现,而不会进入 `objc_msgSend`: -
 因为 `objc_msgSend` 是一个私有方法,我们没有办法进入它的实现,但是,我们却可以在 `objc_msgSend` 的调用栈中“截下”这个函数调用的过程。 @@ -188,7 +183,6 @@ int main(int argc, const char * argv[]) { 在 `objc-runtime-new.mm` 文件中有一个函数 `lookUpImpOrForward`,这个函数的作用就是查找方法的实现,于是运行程序,在运行到 `hello` 这一行时,激活 `lookUpImpOrForward` 函数中的断点。 -
> 由于转成 gif 实在是太大了,笔者试着用各种方法生成动图,然而效果也不是很理想,只能贴一个 Youtube 的视频链接,不过对于能够翻墙的开发者们,应该也不是什么问题吧(手动微笑)
@@ -203,7 +197,6 @@ int main(int argc, const char * argv[]) {
在 `-> [object hello]` 这里增加一个断点,**当程序运行到这一行时**,再向 `lookUpImpOrForward` 函数的第一行添加断点,确保是捕获 `@selector(hello)` 的调用栈,而不是调用其它选择子的调用栈。
-
 由图中的变量区域可以了解,传入的选择子为 `"hello"`,对应的类是 `XXObject`。所以我们可以确信这就是当调用 `hello` 方法时执行的函数。在 Xcode 左侧能看到方法的调用栈: @@ -221,7 +214,7 @@ int main(int argc, const char * argv[]) { ```objectivec IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls) { - return lookUpImpOrForward(cls, sel, obj, + return lookUpImpOrForward(cls, sel, obj, YES/*initialize*/, NO/*cache*/, YES/*resolver*/); } ``` @@ -291,12 +284,10 @@ imp = cache_getImp(cls, sel); if (imp) goto done; ``` -
 不过 `cache_getImp` 的实现目测是不开源的,同时也是汇编写的,在我们尝试 step in 的时候进入了如下的汇编代码。 -
 它会进入一个 `CacheLookup` 的标签,获取实现,使用汇编的原因还是因为要加速整个实现查找的过程,其原理推测是在类的 `cache` 中寻找对应的实现,只是做了一些性能上的优化。 @@ -316,8 +307,8 @@ if (meth) { ```objectivec static method_t *getMethodNoSuper_nolock(Class cls, SEL sel) { - for (auto mlists = cls->data()->methods.beginLists(), - end = cls->data()->methods.endLists(); + for (auto mlists = cls->data()->methods.beginLists(), + end = cls->data()->methods.endLists(); mlists != end; ++mlists) { @@ -336,7 +327,7 @@ static method_t *search_method_list(const method_list_t *mlist, SEL sel) { int methodListIsFixedUp = mlist->isFixedUp(); int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t); - + if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) { return findMethodInSortedMethodList(sel, mlist); } else { @@ -434,11 +425,11 @@ void _class_resolveMethod(Class cls, SEL sel, id inst) { if (! cls->isMetaClass()) { _class_resolveInstanceMethod(cls, sel, inst); - } + } else { _class_resolveClassMethod(cls, sel, inst); - if (!lookUpImpOrNil(cls, sel, inst, - NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + if (!lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) { _class_resolveInstanceMethod(cls, sel, inst); } @@ -450,7 +441,7 @@ void _class_resolveMethod(Class cls, SEL sel, id inst) ```objectivec static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst) { - if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, + if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) { // 没有找到 resolveInstanceMethod: 方法,直接返回。 return; @@ -460,7 +451,7 @@ static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst) { bool resolved = msg(cls, SEL_resolveInstanceMethod, sel); // 缓存结果,以防止下次在调用 resolveInstanceMethod: 方法影响性能。 - IMP imp = lookUpImpOrNil(cls, sel, inst, + IMP imp = lookUpImpOrNil(cls, sel, inst, NO/*initialize*/, YES/*cache*/, NO/*resolver*/); } ``` @@ -486,7 +477,6 @@ cache_fill(cls, sel, imp, inst); 这样就结束了整个方法第一次的调用过程,缓存没有命中,但是在当前类的方法列表中找到了 `hello` 方法的实现,调用了该方法。 -
 @@ -507,7 +497,6 @@ int main(int argc, const char * argv[]) { 然后在第二次调用 `hello` 方法时,加一个断点: -
 `objc_msgSend` 并没有走 `lookupImpOrForward` 这个方法,而是直接结束,打印了另一个 `hello` 字符串。 @@ -518,7 +507,6 @@ int main(int argc, const char * argv[]) { 好,现在重新运行程序至第二个 `hello` 方法调用之前: -
 打印缓存中 bucket 的内容: @@ -568,12 +556,10 @@ int main(int argc, const char * argv[]) { } ``` -
 这样 `XXObject` 中就不存在 `hello` 方法对应实现的缓存了。然后继续运行程序: -
 虽然第二次调用 `hello` 方法,但是因为我们清除了 `hello` 的缓存,所以,会再次进入 `lookupImpOrForward` 方法。 @@ -604,12 +590,10 @@ int main(int argc, const char * argv[]) { 在第一个 `hello` 方法调用之前将实现加入缓存: -
 然后继续运行代码: -
 可以看到,我们虽然没有改变 `hello` 方法的实现,但是在 **objc_msgSend** 的消息发送链路中,使用错误的缓存实现 `cached_imp` 拦截了实现的查找,打印出了 `Cached Hello`。 @@ -625,7 +609,7 @@ int main(int argc, const char * argv[]) { 这篇文章与其说是讲 ObjC 中的消息发送的过程,不如说是讲方法的实现是如何查找的。 Objective-C 中实现查找的路径还是比较符合直觉的: - + 1. 缓存命中 2. 查找当前类的缓存及方法 3. 查找父类的缓存及方法 @@ -641,5 +625,3 @@ Objective-C 中实现查找的路径还是比较符合直觉的: + [Let's Build objc_msgSend](https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html) Follow: [@Draveness](https://github.com/Draveness) - - diff --git "a/contents/objc/\344\275\240\347\234\237\347\232\204\344\272\206\350\247\243 load \346\226\271\346\263\225\344\271\210\357\274\237.md" "b/contents/objc/\344\275\240\347\234\237\347\232\204\344\272\206\350\247\243 load \346\226\271\346\263\225\344\271\210\357\274\237.md" index a8135eb..60dd61e 100644 --- "a/contents/objc/\344\275\240\347\234\237\347\232\204\344\272\206\350\247\243 load \346\226\271\346\263\225\344\271\210\357\274\237.md" +++ "b/contents/objc/\344\275\240\347\234\237\347\232\204\344\272\206\350\247\243 load \346\226\271\346\263\225\344\271\210\357\274\237.md" @@ -52,7 +52,7 @@ int main(int argc, const char * argv[]) { 代码总共只实现了一个 `XXObject` 的 `+ load` 方法,主函数中也没有任何的东西: -
 + 虽然在主函数中什么方法都没有调用,但是运行之后,依然打印了 `XXObject load` 字符串,也就是说调用了 `+ load` 方法。 @@ -62,13 +62,13 @@ int main(int argc, const char * argv[]) { > 注意这里 `+` 和 `[` 之间没有空格 -
 + > 为什么要加一个符号断点呢?因为这样看起来比较高级。 重新运行程序。这时,代码会停在 `NSLog(@"XXObject load");` 这一行的实现上: -
 + 左侧的调用栈很清楚的告诉我们,哪些方法被调用了: @@ -128,7 +128,7 @@ load_images(enum dyld_image_states state, uint32_t infoCount, 这里就会遇到一个问题:镜像到底是什么,我们用一个断点打印出所有加载的镜像: -
 + 从控制台输出的结果大概就是这样的,我们可以看到镜像并不是一个 Objective-C 的代码文件,它应该是一个 target 的编译产物。 @@ -160,7 +160,7 @@ load_images(enum dyld_image_states state, uint32_t infoCount, 但是如果进入最下面的这个目录,会发现它是一个**可执行文件**,它的运行结果与 Xcode 中的运行结果相同: -
 + ### 准备 + load 方法 @@ -267,7 +267,7 @@ void call_load_methods(void) 方法的调用流程大概是这样的: -
 + 其中 `call_class_loads` 会从一个待加载的类列表 `loadable_classes` 中寻找对应的类,然后找到 `@selector(load)` 的实现并执行。 @@ -310,7 +310,7 @@ ObjC 对于加载的管理,主要使用了两个列表,分别是 `loadable_c 方法的调用过程也分为两个部分,准备 `load` 方法和调用 `load` 方法,我更觉得这两个部分比较像生产者与消费者: -
 + `add_class_to_loadable_list` 方法负责将类加入 `loadable_classes` 集合,而 `call_class_loads` 负责消费集合中的元素。 diff --git "a/contents/objc/\346\267\261\345\205\245\350\247\243\346\236\220 ObjC \344\270\255\346\226\271\346\263\225\347\232\204\347\273\223\346\236\204.md" "b/contents/objc/\346\267\261\345\205\245\350\247\243\346\236\220 ObjC \344\270\255\346\226\271\346\263\225\347\232\204\347\273\223\346\236\204.md" index ba3dfa5..17d666a 100644 --- "a/contents/objc/\346\267\261\345\205\245\350\247\243\346\236\220 ObjC \344\270\255\346\226\271\346\263\225\347\232\204\347\273\223\346\236\204.md" +++ "b/contents/objc/\346\267\261\345\205\245\350\247\243\346\236\220 ObjC \344\270\255\346\226\271\346\263\225\347\232\204\347\273\223\346\236\204.md" @@ -10,7 +10,6 @@ 先来了解一下 ObjC 中类的结构图: -
 + `isa` 是指向元类的指针,不了解元类的可以看 [Classes and Metaclasses](http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html) @@ -24,7 +23,6 @@ 下面就是 ObjC 中 `class_data_bits_t` 的结构体,其中只含有一个 64 位的 `bits` 用于存储与类有关的信息: -
 在 `objc_class` 结构体中的注释写到 `class_data_bits_t` 相当于 `class_rw_t` 指针加上 rr/alloc 的标志。 @@ -47,7 +45,6 @@ class_rw_t* data() { 因为 `class_rw_t *` 指针只存于第 `[3, 47]` 位,所以可以使用最后三位来存储关于当前类的其他信息: -
 @@ -126,7 +123,6 @@ struct class_ro_t { **在编译期间**类的结构中的 `class_data_bits_t *data` 指向的是一个 `class_ro_t *` 指针: -
 @@ -147,7 +143,6 @@ cls->setData(rw); 下图是 `realizeClass` 方法执行过后的类所占用内存的布局,你可以与上面调用方法前的内存布局对比以下,看有哪些更改: -
 但是,在这段代码运行之后 `class_rw_t` 中的方法,属性以及协议列表均为空。这时需要 `realizeClass` 调用 `methodizeClass` 方法来**将类自己实现的方法(包括分类)、属性和遵循的协议加载到 `methods`、 `properties` 和 `protocols` 列表中**。 @@ -181,7 +176,6 @@ cls->setData(rw); > 这段代码是运行在 Mac OS X 10.11.3 (x86_64)版本中,而不是运行在 iPhone 模拟器或者真机上的,如果你在 iPhone 或者真机上运行,可能有一定差别。 -
 这是主程序的代码: @@ -209,7 +203,6 @@ int main(int argc, const char * argv[]) { 接下来,在整个 ObjC 运行时初始化之前,也就是 `_objc_init` 方法中加入一个断点: -
 然后在 lldb 中输入以下命令: @@ -240,7 +233,6 @@ warning: could not load any Objective-C class information. This will significant } ``` -
 现在我们获取了类经过编译器处理后的只读属性 `class_ro_t`: @@ -281,7 +273,6 @@ The process has been returned to the state before expression evaluation. (lldb) ``` -
 使用 `$5->get(0)` 时,成功获取到了 `-[XXObject hello]` 方法的结构体 `method_t`。而尝试获取下一个方法时,断言提示我们当前类只有一个方法。 @@ -299,14 +290,12 @@ static Class realizeClass(Class cls) 上面就是这个方法的签名,我们需要在这个方法中打一个条件断点,来判断当前类是否为 `XXObject`: -
 这里直接判断两个指针是否相等,而不使用 `[NSStringFromClass(cls) isEqualToString:@"XXObject"]` 是因为在这个时间点,这些方法都不能调用,在 ObjC 中没有这些方法,所以只能通过判断类指针是否相等的方式来确认当前类是 `XXObject`。 > 直接与指针比较是因为类在内存中的位置是编译期确定的,只要代码不改变,类在内存中的位置就会不变(已经说过很多遍了)。 -
 @@ -316,13 +305,11 @@ static Class realizeClass(Class cls) 在这时打印类结构体中的 `data` 的值,发现其中的布局依旧是这样的: -
 在运行完这段代码之后: -
 我们再来打印类的结构: @@ -400,7 +387,6 @@ Assertion failed: (i < count), function get, file /Users/apple/Desktop/objc-runt (lldb) ``` -
 > 最后一个操作实在是截取不到了 @@ -415,7 +401,6 @@ cls->setData(rw); 在上述的代码运行之后,类的只读指针 `class_ro_t` 以及可读写指针 `class_rw_t` 都被正确的设置了。但是到这里,其 `class_rw_t` 部分的方法等成员的指针 `methods`、 `protocols` 和 `properties` 均为空,这些会在 `methodizeClass` 中进行设置: -
 在这里调用了 `method_array_t` 的 `attachLists` 方法,将 `baseMethods` 中的方法添加到 `methods` 数组之后。我们访问 `methods` 才会获取当前类的实例方法。 @@ -434,7 +419,6 @@ struct method_t { 其中包含方法名,类型还有方法的实现指针 `IMP`: -

上面的 `-[XXObject hello]` 方法的结构体是这样的:
diff --git a/contents/rack/images/rack-thin/event-callback.png b/contents/rack/images/rack-thin/event-callback.png
new file mode 100644
index 0000000..97e6423
Binary files /dev/null and b/contents/rack/images/rack-thin/event-callback.png differ
diff --git a/contents/rack/images/rack-thin/eventmachine-select.png b/contents/rack/images/rack-thin/eventmachine-select.png
new file mode 100644
index 0000000..8673de9
Binary files /dev/null and b/contents/rack/images/rack-thin/eventmachine-select.png differ
diff --git a/contents/rack/images/rack-thin/reactor-eventloop.png b/contents/rack/images/rack-thin/reactor-eventloop.png
new file mode 100644
index 0000000..eed3171
Binary files /dev/null and b/contents/rack/images/rack-thin/reactor-eventloop.png differ
diff --git a/contents/rack/images/rack-thin/selectable-and-subclasses.png b/contents/rack/images/rack-thin/selectable-and-subclasses.png
new file mode 100644
index 0000000..af1891c
Binary files /dev/null and b/contents/rack/images/rack-thin/selectable-and-subclasses.png differ
diff --git a/contents/rack/images/rack-thin/thin-handle-request.png b/contents/rack/images/rack-thin/thin-handle-request.png
new file mode 100644
index 0000000..aaec961
Binary files /dev/null and b/contents/rack/images/rack-thin/thin-handle-request.png differ
diff --git a/contents/rack/images/rack-thin/thin-initialize-server.png b/contents/rack/images/rack-thin/thin-initialize-server.png
new file mode 100644
index 0000000..515c9d0
Binary files /dev/null and b/contents/rack/images/rack-thin/thin-initialize-server.png differ
diff --git a/contents/rack/images/rack-thin/thin-io-model.png b/contents/rack/images/rack-thin/thin-io-model.png
new file mode 100644
index 0000000..a5846b3
Binary files /dev/null and b/contents/rack/images/rack-thin/thin-io-model.png differ
diff --git a/contents/rack/images/rack-thin/thin-send-response.png b/contents/rack/images/rack-thin/thin-send-response.png
new file mode 100644
index 0000000..0685d5b
Binary files /dev/null and b/contents/rack/images/rack-thin/thin-send-response.png differ
diff --git a/contents/rack/images/rack-thin/thin-start-server.png b/contents/rack/images/rack-thin/thin-start-server.png
new file mode 100644
index 0000000..ebdc972
Binary files /dev/null and b/contents/rack/images/rack-thin/thin-start-server.png differ
diff --git a/contents/rack/images/rack-unicorn/unicorn-daemonize.png b/contents/rack/images/rack-unicorn/unicorn-daemonize.png
new file mode 100644
index 0000000..2d4bb0b
Binary files /dev/null and b/contents/rack/images/rack-unicorn/unicorn-daemonize.png differ
diff --git a/contents/rack/images/rack-unicorn/unicorn-io-model.png b/contents/rack/images/rack-unicorn/unicorn-io-model.png
new file mode 100644
index 0000000..b25d078
Binary files /dev/null and b/contents/rack/images/rack-unicorn/unicorn-io-model.png differ
diff --git a/contents/rack/images/rack-unicorn/unicorn-multi-processes.png b/contents/rack/images/rack-unicorn/unicorn-multi-processes.png
new file mode 100644
index 0000000..423398a
Binary files /dev/null and b/contents/rack/images/rack-unicorn/unicorn-multi-processes.png differ
diff --git a/contents/rack/images/rack-unicorn/unicorn.jpeg b/contents/rack/images/rack-unicorn/unicorn.jpeg
new file mode 100644
index 0000000..6df4100
Binary files /dev/null and b/contents/rack/images/rack-unicorn/unicorn.jpeg differ
diff --git a/contents/rack/images/rack-webrick/mounttable-and-applications.png b/contents/rack/images/rack-webrick/mounttable-and-applications.png
new file mode 100644
index 0000000..0a3b6b3
Binary files /dev/null and b/contents/rack/images/rack-webrick/mounttable-and-applications.png differ
diff --git a/contents/rack/images/rack-webrick/webrick-io-model.png b/contents/rack/images/rack-webrick/webrick-io-model.png
new file mode 100644
index 0000000..4092d1b
Binary files /dev/null and b/contents/rack/images/rack-webrick/webrick-io-model.png differ
diff --git a/contents/rack/images/rack/rack-and-web-servers-frameworks.png b/contents/rack/images/rack/rack-and-web-servers-frameworks.png
new file mode 100644
index 0000000..51163df
Binary files /dev/null and b/contents/rack/images/rack/rack-and-web-servers-frameworks.png differ
diff --git a/contents/rack/images/rack/rack-app.png b/contents/rack/images/rack/rack-app.png
new file mode 100644
index 0000000..c2c7966
Binary files /dev/null and b/contents/rack/images/rack/rack-app.png differ
diff --git a/contents/rack/images/rack/rack-logo.png b/contents/rack/images/rack/rack-logo.png
new file mode 100644
index 0000000..f5bba1a
Binary files /dev/null and b/contents/rack/images/rack/rack-logo.png differ
diff --git a/contents/rack/images/rack/rack-protocol.png b/contents/rack/images/rack/rack-protocol.png
new file mode 100644
index 0000000..b5ae951
Binary files /dev/null and b/contents/rack/images/rack/rack-protocol.png differ
diff --git a/contents/rack/images/rack/rails-application.png b/contents/rack/images/rack/rails-application.png
new file mode 100644
index 0000000..0fe07d9
Binary files /dev/null and b/contents/rack/images/rack/rails-application.png differ
diff --git a/contents/rack/images/rack/server-app-call-stack.png b/contents/rack/images/rack/server-app-call-stack.png
new file mode 100644
index 0000000..8dde02d
Binary files /dev/null and b/contents/rack/images/rack/server-app-call-stack.png differ
diff --git a/contents/rack/images/rack/wrapped-app.png b/contents/rack/images/rack/wrapped-app.png
new file mode 100644
index 0000000..2b0e12a
Binary files /dev/null and b/contents/rack/images/rack/wrapped-app.png differ
diff --git a/contents/rack/rack-thin.md b/contents/rack/rack-thin.md
new file mode 100644
index 0000000..99ceaf9
--- /dev/null
+++ b/contents/rack/rack-thin.md
@@ -0,0 +1,786 @@
+# 浅谈 Thin 的事件驱动模型
+
++ [谈谈 Rack 协议与实现](https://draveness.me/rack)
++ [浅谈 WEBrick 的实现](https://draveness.me/rack-webrick)
++ [浅谈 Thin 的事件驱动模型](https://draveness.me/rack-thin)
++ [浅谈 Unicorn 的多进程模型](https://draveness.me/rack-unicorn)
++ [浅谈 Puma 的实现](https://draveness.me/rack-puma)
+
+在上一篇文章中我们已经介绍了 WEBrick 的实现,它的 handler 是写在 Rack 工程中的,而在这篇文章介绍的 webserver [thin](https://github.com/macournoyer/thin) 的 Rack 处理器也是写在 Rack 中的;与 WEBrick 相同,Thin 的实现也非常简单,官方对它的介绍是:
+
+> A very fast & simple Ruby web server.
+
+它将 [Mongrel](https://zedshaw.com/archive/ragel-state-charts/)、[Event Machine](https://github.com/eventmachine/eventmachine) 和 [Rack](http://rack.github.io) 三者进行组合,在其中起到胶水的作用,所以在理解 Thin 的实现的过程中我们也需要分析 EventMachine 到底是如何工作的。
+
+## Thin 的实现
+
+在这一节中我们将从源代码来分析介绍 Thin 的实现原理,因为部分代码仍然是在 Rack 工程中实现的,所以我们要从 Rack 工程的代码开始理解 Thin 的实现。
+
+### 从 Rack 开始
+
+Thin 的处理器 `Rack::Handler::Thin` 与其他遵循 Rack 协议的 webserver 一样都实现了 `.run` 方法,接受 Rack 应用和 `options` 作为输入:
+
+```ruby
+module Rack
+ module Handler
+ class Thin
+ def self.run(app, options={})
+ environment = ENV['RACK_ENV'] || 'development'
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
+ host = options.delete(:Host) || default_host
+ port = options.delete(:Port) || 8080
+ args = [host, port, app, options]
+ args.pop if ::Thin::VERSION::MAJOR < 1 && ::Thin::VERSION::MINOR < 8
+ server = ::Thin::Server.new(*args)
+ yield server if block_given?
+ server.start
+ end
+ end
+ end
+end
+```
+
+上述方法仍然会从 `options` 中取出 ip 地址和端口号,然后初始化一个 `Thin::Server` 的实例后,执行 `#start` 方法在 8080 端口上监听来自用户的请求。
+
+### 初始化服务
+
+Thin 服务的初始化由以下的代码来处理,首先会处理在 `Rack::Handler::Thin.run` 中传入的几个参数 `host`、`port`、`app` 和 `options`,将 Rack 应用存储在临时变量中:
+
+```ruby
+From: lib/thin/server.rb @ line 100:
+Owner: Thin::Server
+
+def initialize(*args, &block)
+ host, port, options = DEFAULT_HOST, DEFAULT_PORT, {}
+
+ args.each do |arg|
+ case arg
+ when 0.class, /^\d+$/ then port = arg.to_i
+ when String then host = arg
+ when Hash then options = arg
+ else
+ @app = arg if arg.respond_to?(:call)
+ end
+ end
+
+ @backend = select_backend(host, port, options)
+ @backend.server = self
+ @backend.maximum_connections = DEFAULT_MAXIMUM_CONNECTIONS
+ @backend.maximum_persistent_connections = DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
+ @backend.timeout = options[:timeout] || DEFAULT_TIMEOUT
+
+ @app = Rack::Builder.new(&block).to_app if block
+end
+```
+
+在初始化服务的过程中,总共只做了三件事情,处理参数、选择并配置 `backend`,创建新的应用:
+
+
+
+处理参数的过程自然不用多说,只是这里判断的方式并不是按照顺序处理的,而是按照参数的类型;在初始化器的最后,如果向初始化器传入了 block,那么就会使用 `Rack::Builder` 和 block 中的代码初始化一个新的 Rack 应用。
+
+### 选择后端
+
+在选择后端时 Thin 使用了 `#select_backend` 方法,这里使用 `case` 语句替代多个 `if`、`else`,也是一个我们可以使用的小技巧:
+
+```ruby
+From: lib/thin/server.rb @ line 261:
+Owner: Thin::Server
+
+def select_backend(host, port, options)
+ case
+ when options.has_key?(:backend)
+ raise ArgumentError, ":backend must be a class" unless options[:backend].is_a?(Class)
+ options[:backend].new(host, port, options)
+ when options.has_key?(:swiftiply)
+ Backends::SwiftiplyClient.new(host, port, options)
+ when host.include?('/')
+ Backends::UnixServer.new(host)
+ else
+ Backends::TcpServer.new(host, port)
+ end
+end
+```
+
+在大多数时候,我们只会选择 `UnixServer` 和 `TcpServer` 两种后端中的一个,而后者又是两者中使用更为频繁的后端:
+
+```ruby
+From: lib/thin/backends/tcp_server.rb @ line 8:
+Owner: Thin::Backends::TcpServer
+
+def initialize(host, port)
+ @host = host
+ @port = port
+ super()
+end
+
+From: lib/thin/backends/base.rb @ line 47:
+Owner: Thin::Backends::Base
+
+def initialize
+ @connections = {}
+ @timeout = Server::DEFAULT_TIMEOUT
+ @persistent_connection_count = 0
+ @maximum_connections = Server::DEFAULT_MAXIMUM_CONNECTIONS
+ @maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
+ @no_epoll = false
+ @ssl = nil
+ @threaded = nil
+ @started_reactor = false
+end
+```
+
+初始化的过程中只是对属性设置默认值,比如 `host`、`port` 以及超时时间等等,并没有太多值得注意的代码。
+
+### 启动服务
+
+在启动服务时会直接调用 `TcpServer#start` 方法并在其中传入一个用于处理信号的 block:
+
+```ruby
+From: lib/thin/server.rb @ line 152:
+Owner: Thin::Server
+
+def start
+ raise ArgumentError, 'app required' unless @app
+
+ log_info "Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
+ log_debug "Debugging ON"
+ trace "Tracing ON"
+
+ log_info "Maximum connections set to #{@backend.maximum_connections}"
+ log_info "Listening on #{@backend}, CTRL+C to stop"
+
+ @backend.start { setup_signals if @setup_signals }
+end
+```
+
+虽然这里的 `backend` 其实已经被选择成了 `TcpServer`,但是该子类并没有覆写 `#start` 方法,这里执行的方法其实是从父类继承的:
+
+```ruby
+From: lib/thin/backends/base.rb @ line 60:
+Owner: Thin::Backends::Base
+
+def start
+ @stopping = false
+ starter = proc do
+ connect
+ yield if block_given?
+ @running = true
+ end
+
+ # Allow for early run up of eventmachine.
+ if EventMachine.reactor_running?
+ starter.call
+ else
+ @started_reactor = true
+ EventMachine.run(&starter)
+ end
+end
+```
+
+上述方法在构建一个 `starter` block 之后,将该 block 传入 `EventMachine.run` 方法,随后执行的 `#connect` 会启动一个 `EventMachine` 的服务器用于处理用户的网络请求:
+
+```ruby
+From: lib/thin/backends/tcp_server.rb @ line 15:
+Owner: Thin::Backends::TcpServer
+
+def connect
+ @signature = EventMachine.start_server(@host, @port, Connection, &method(:initialize_connection))
+ binary_name = EventMachine.get_sockname( @signature )
+ port_name = Socket.unpack_sockaddr_in( binary_name )
+ @port = port_name[0]
+ @host = port_name[1]
+ @signature
+end
+```
+
+在 EventMachine 的文档中,`.start_server` 方法被描述成一个在指定的地址和端口上初始化 TCP 服务的方法,正如这里所展示的,它经常在 `.run` 方法的 block 中执行;该方法的参数 `Connection` 作为处理 TCP 请求的类,会实现不同的方法接受各种各样的回调,传入的 `initialize_connection` block 会在有请求需要处理时对 `Connection` 对象进行初始化:
+
+> `Connection` 对象继承自 `EventMachine::Connection`,是 EventMachine 与外界的接口,在 EventMachine 中的大部分事件都会调用 `Connection` 的一个实例方法来传递数据和参数。
+
+```ruby
+From: lib/thin/backends/base.rb @ line 145:
+Owner: Thin::Backends::Base
+
+def initialize_connection(connection)
+ connection.backend = self
+ connection.app = @server.app
+ connection.comm_inactivity_timeout = @timeout
+ connection.threaded = @threaded
+ connection.start_tls(@ssl_options) if @ssl
+
+ if @persistent_connection_count < @maximum_persistent_connections
+ connection.can_persist!
+ @persistent_connection_count += 1
+ end
+ @connections[connection.__id__] = connection
+end
+```
+
+### 处理请求的连接
+
+`Connection` 类中有很多的方法 `#post_init`、`#receive_data` 方法等等都是由 EventMachine 在接收到请求时调用的,当 Thin 的服务接收到来自客户端的数据时就会调用 `#receive_data` 方法:
+
+```ruby
+From: lib/thin/connection.rb @ line 36:
+Owner: Thin::Connection
+
+def receive_data(data)
+ @idle = false
+ trace data
+ process if @request.parse(data)
+rescue InvalidRequest => e
+ log_error("Invalid request", e)
+ post_process Response::BAD_REQUEST
+end
+```
+
+在这里我们看到了与 WEBrick 在处理来自客户端的原始数据时使用的方法 `#parse`,它会解析客户端请求的原始数据并执行 `#process` 来处理 HTTP 请求:
+
+```ruby
+From: lib/thin/connection.rb @ line 47:
+Owner: Thin::Connection
+
+def process
+ if threaded?
+ @request.threaded = true
+ EventMachine.defer { post_process(pre_process) }
+ else
+ @request.threaded = false
+ post_process(pre_process)
+ end
+end
+```
+
+如果当前的连接允许并行处理多个用户的请求,那么就会在 `EventMachine.defer` 的 block 中执行两个方法 `#pre_process` 和 `#post_process`:
+
+```ruby
+From: lib/thin/connection.rb @ line 63:
+Owner: Thin::Connection
+
+def pre_process
+ @request.remote_address = remote_address
+ @request.async_callback = method(:post_process)
+
+ response = AsyncResponse
+ catch(:async) do
+ response = @app.call(@request.env)
+ end
+ response
+rescue Exception => e
+ unexpected_error(e)
+ can_persist? && @request.persistent? ? Response::PERSISTENT_ERROR : Response::ERROR
+end
+```
+
+在 `#pre_process` 中没有做太多的事情,只是调用了 Rack 应用的 `#call` 方法,得到一个三元组 `response`,在这之后将这个数组传入 `#post_process` 方法:
+
+```ruby
+From: lib/thin/connection.rb @ line 95:
+Owner: Thin::Connection
+
+def post_process(result)
+ return unless result
+ result = result.to_a
+ return if result.first == AsyncResponse.first
+
+ @response.status, @response.headers, @response.body = *result
+ @response.each do |chunk|
+ send_data chunk
+ end
+rescue Exception => e
+ unexpected_error(e)
+ close_connection
+ensure
+ if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback)
+ @response.body.callback { terminate_request }
+ @response.body.errback { terminate_request }
+ else
+ terminate_request unless result && result.first == AsyncResponse.first
+ end
+end
+```
+
+`#post_response` 方法将传入的数组赋值给 `response` 的 `status`、`headers` 和 `body` 这三部分,在这之后通过 `#send_data` 方法将 HTTP 响应以块的形式写回 Socket;写回结束后可能会调用对应的 `callback` 并关闭持有的 `request` 和 `response` 两个实例变量。
+
+> 上述方法中调用的 `#send_data` 继承自 `EventMachine::Connection` 类。
+
+### 小结
+
+到此为止,我们对于 Thin 是如何处理来自用户的 HTTP 请求的就比较清楚了,我们可以看到 Thin 本身并没有做一些类似解析 HTTP 数据包以及发送数据的问题,它使用了来自 Rack 和 EventMachine 两个开源框架中很多已有的代码逻辑,确实只做了一些胶水的事情。
+
+对于 Rack 是如何工作的我们在前面的文章 [谈谈 Rack 协议与实现](https://draveness.me/rack) 中已经介绍过了;虽然我们看到了很多与 EventMachine 相关的代码,但是到这里我们仍然对 EventMachine 不是太了解。
+
+## EventMachine 和 Reactor 模式
+
+为了更好地理解 Thin 的工作原理,在这里我们会介绍一个 EventMachine 和 Reactor 模式。
+
+EventMachine 其实是一个使用 Ruby 实现的事件驱动的并行框架,它使用 Reactor 模式提供了事件驱动的 IO 模型,如果你对 Node.js 有所了解的话,那么你一定对事件驱动这个词并不陌生,EventMachine 的出现主要是为了解决两个核心问题:
+
++ 为生产环境提供更高的可伸缩性、更好的性能和稳定性;
++ 为上层提供了一些能够减少高性能的网络编程复杂性的 API;
+
+其实 EventMachine 的主要作用就是将所有同步的 IO 都变成异步的,调度都通过事件来进行,这样用于监听用户请求的进程不会被其他代码阻塞,能够同时为更多的客户端提供服务;在这一节中,我们需要了解一下在 Thin 中使用的 EventMachine 中几个常用方法的实现。
+
+### 启动事件循环
+
+EventMachine 其实就是一个事件循环(Event Loop),当我们想使用 EventMachine 来处理某些任务时就一定需要调用 `.run` 方法启动这个事件循环来接受外界触发的各种事件:
+
+```ruby
+From: lib/eventmachine.rb @ line 149:
+Owner: #