<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Objective-C on Chen</title><link>https://hechen.github.io/zh-cn/tags/objective-c/</link><description>Recent content in Objective-C on Chen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 16 Jul 2018 17:34:36 +0000</lastBuildDate><atom:link href="https://hechen.github.io/zh-cn/tags/objective-c/index.xml" rel="self" type="application/rss+xml"/><item><title>避免在 Swift 中使用单例</title><link>https://hechen.github.io/zh-cn/post/avoiding-singletons-in-swift/</link><pubDate>Mon, 16 Jul 2018 17:34:36 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/avoiding-singletons-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/avoiding-singletons-in-swift/"&gt;Avoiding singletons in Swift&lt;/a&gt;
原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;“我知道单例不好，但是…”，这是开发者常常在讨论代码的时候会提到的。貌似社区大家有共识 ── 单例不好。但是同时，包括 Apple 和第三方的 Swift 开发者还是在 App 内部或者共享的 &lt;code&gt;frameworks&lt;/code&gt; 里不断在用它们。&lt;/p&gt;
&lt;p&gt;这周，让我们好好看一看单例的问题到底在哪里，探索更多的技巧以便我们日常能够避免使用它们。这就开始吧！&lt;/p&gt;
&lt;h3 id="为什么单例这么流行"&gt;为什么单例这么流行？&lt;/h3&gt;
&lt;p&gt;首先一点我们要想的是问问自己，为什么单例这么流行？热过大多数开发者都认同，单例应该被避免的话，为什么它们还不断的出现呢？&lt;/p&gt;
&lt;p&gt;我认为有两部分原因。&lt;/p&gt;
&lt;p&gt;我觉得日常开发 Apple 平台上的 App 时候大量使用单例，最大的原因是苹果自己也在经常的用。作为第三方开发者来讲，我们常常会把 Apple 的做法作为最佳实践来用，任何 Apple 自己常常使用的模式在社区也会发展的很迅速。&lt;/p&gt;
&lt;p&gt;这个谜题的第二个原因是单例非常方便。单例因为它可以在任何地方获取到，因而经常会扮演一个获取特定核心值或者对象的快捷方式。可以看下下面这个例子，在这个例子中，我们想在 &lt;strong&gt;ProfileViewController&lt;/strong&gt; 中展示当前登录用户的名字，同时当某个按钮按下的时候，退登当前用户。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kr"&gt;lazy&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;nameLabel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UILabel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nameLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;handleLogOutButtonTap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;UserManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;按照上面这样，把一个 UserManager 单例的用户和账户处理的功能封装起来，确实很方便（而且很常见！），那这种模式的使用到底不好在哪里呢？&lt;/p&gt;
&lt;h3 id="为什么单例会很糟糕"&gt;为什么单例会很糟糕？&lt;/h3&gt;
&lt;p&gt;当讨论类似模式和架构这些东西的时候，很容易陷入一个特别理论化的圈套。虽然让我们的代码理论上“正确”，遵循最佳实践和原则是很美好的，但是现实经常会很受打击，我们需要寻找一些中间方案。&lt;/p&gt;
&lt;p&gt;所以，关于单例引起的实质性问题是什么呢？为什么它们要应该被避免呢？这里我倾向于三个原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;这些单例是全局的可变的共享状态。它们的状态是在整个 App 里自动共享的，因此当其状态发生不可预期的变化的时候，Bug 也就可能开始发生了；&lt;/li&gt;
&lt;li&gt;单例之间的关系以及依赖这些单例的代码常常不那么好定义。因为单例是如此方便和容易获得，大量的使用它们常常会导致很难维护&lt;a href="https://zh.wikipedia.org/wiki/%E9%9D%A2%E6%9D%A1%E5%BC%8F%E4%BB%A3%E7%A0%81"&gt;面条式代码&lt;/a&gt;，这些代码中对象和对象之间没有清晰的隔离；&lt;/li&gt;
&lt;li&gt;管理这些单例的生命周期非常的难。因为单例本身会在应用的整个生命周期内存活，管理它们就变得异常困难，因此常常会不得不依赖可选值来跟踪值的变化。这也使得依赖单例的代码变得很难测试，因为你没法在一个测试 case 里把状态重置。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在我们前面的 &lt;strong&gt;ProfileViewController&lt;/strong&gt; 的例子中，我们已经看到了有这三个问题的信号出现。首先它依赖 UserManager 导致这二者关系很不清晰，其次，它不得不让 currentUser 作为可选值出现，因为我们没办法在编译期就能够确定视图控制器实际上出现的时候这个数据一定在。听起来就感觉要有 bug 发生的感觉 😬!&lt;/p&gt;
&lt;h3 id="依赖注入"&gt;依赖注入&lt;/h3&gt;
&lt;p&gt;相对于使用 &lt;strong&gt;ProfileViewController&lt;/strong&gt; 通过单例来获取它所需的依赖项，我们要在其初始化方法中将依赖项传入。这里我们是将当前的 User 作为非可选传入的，同样，传入一个 &lt;strong&gt;LogOutService&lt;/strong&gt; 来进行登出操作：&lt;/p&gt;</description></item><item><title>Swift 中几种不同的依赖注入方式</title><link>https://hechen.github.io/zh-cn/post/different-flavors-of-dependency-injection-in-swift/</link><pubDate>Mon, 16 Jul 2018 13:57:24 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/different-flavors-of-dependency-injection-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/different-flavors-of-dependency-injection-in-swift/"&gt;Different flavors of dependency injection in Swift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在之前的几篇博客中，我们已经了解了几种使用依赖注入方式使得某个 swift app 拥有一个更加解耦可测试的架构。比如在 &lt;a href="https://hechen.github.io/2017/11/19/Dependency-injection-using-factories-in-Swift/"&gt;在 Swift 中使用工厂模式进行依赖注入&lt;/a&gt; 中和工厂模式结合，以及在&lt;a href="https://hechen.github.io/2018/07/16/Avoiding-singletons-in-Swift/"&gt;避免在 Swift 中使用单例&lt;/a&gt;中替换程序中的单例对象等方式进行依赖注入。&lt;/p&gt;
&lt;p&gt;到目前为止，大部分我的博文以及例子中都使用了基于初始化的依赖注入方式。然而，就像大部分的编程技巧一样，还有很多 “口味” 的进行依赖注入的方式 ── 每一种都有其优缺点。 这周，让我们来看看其中三种方式以及如何在 Swift 中使用它们。&lt;/p&gt;
&lt;h3 id="基于初始化方法"&gt;基于初始化方法&lt;/h3&gt;
&lt;p&gt;让我们快速回顾一下用的最为广泛的依赖注入方式 ── initializer-based。这个想法是在某个类被初始化的时候传入其所需的依赖的方式。这种形式最大的好处就是，它保证了能够立即满足该类完成其功能所需要的所有东西。&lt;/p&gt;
&lt;p&gt;让我们建一个类 ── FileLoader 来从磁盘加载文件。为了实现该 Loader 的功能，需要两个依赖项，系统所提供的实例对象 ── &lt;strong&gt;FileManager&lt;/strong&gt; 和 &lt;strong&gt;Cache&lt;/strong&gt;。使用基于初始化的依赖注入的执行代码如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileLoader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;fileManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FileManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FileManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;init&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fileManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以关注下初始化方法里默认参数的设置，其可以避免每次都需要自行创建的问题。这样能够简化我们在生产环境下使用 &lt;strong&gt;FileLoader&lt;/strong&gt; 类创建文件加载器的工作，而且也能够能够传入 mock 数据或者我们的测试代码中的创建的实例进行测试。&lt;/p&gt;
&lt;h3 id="基于属性"&gt;基于属性&lt;/h3&gt;
&lt;p&gt;虽然前一种基于初始化的依赖注入形式常常给我们自定义类带来很多好处，但是有时候，当你不得不继承自某个系统类的时候会遇到一些困难。其中一个例子就是，当我们构建 view controller，尤其是你在使用 XIB 以及 Storyboard 类定义它们的时候，因为此时你不再能够掌控它们的初始化方法了。&lt;/p&gt;
&lt;p&gt;对于这些情况，基于属性的依赖注入方式是一个更好的选择。相对于初始化的时候进行对象的依赖项注入，基于属性的形式可以在之后通过简单的赋值来做到，其能够让你减少模板代码的书写，尤其是在你确实没有必要进行注入的时候能够有更好的默认实现。&lt;/p&gt;
&lt;p&gt;让我们再来看一个例子，在这个例子中，我们在构建一个 &lt;strong&gt;PhotoEditorViewController&lt;/strong&gt; ，这个视图控制器使得用户编辑它们照片库中的图片。为了达到这个功能，该视图控制器需要用到系统提供的 PHPhotoLibrary 类型的一个实例（该类本身是一个单例）以及我们自己实现的类 &lt;strong&gt;PhotoEditorEngine&lt;/strong&gt; 的一个实例。那为了不通过自定义初始化方法进行依赖注入的话，我们可以创建一些具有默认值的可变属性，例如下面这样：&lt;/p&gt;</description></item><item><title>What is LLVM</title><link>https://hechen.github.io/zh-cn/post/what-is-llvm/</link><pubDate>Tue, 10 Jul 2018 12:05:58 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/what-is-llvm/</guid><description>&lt;h1 id="什么是-llvm隐藏在-swiftrustclang-等语言背后的奥秘"&gt;什么是 LLVM？隐藏在 Swift，Rust，Clang 等语言背后的奥秘&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;了解编译器是如何生成机器原生代码会使得倒腾新语言或者加强已经存在的编程语言变得比以往更加容易了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;新的编程语言，针对现存语言的优化如同雨后春笋般产生。 Mozilla 的 &lt;strong&gt;Rust&lt;/strong&gt;， Apple 的 &lt;strong&gt;Swift&lt;/strong&gt;，JetBrain 的 &lt;strong&gt;Kotlin&lt;/strong&gt;等语言给开发者提供了在速度，安全性，便捷性，可移植性以及性能等方面更多的选择。&lt;/p&gt;
&lt;p&gt;为什么是这个时间点呢？一个重大的原因是那些构建具体语言相关的新式工具的出现。在这一堆工具中，最重要的就是 LLVM，其全称是 Low-Level Virtual Machine，它是一个开源项目，其最早是由 Swift 语言的发明人 Chris Lattner 在伊利诺斯州州立大学期间的一个研究项目而来。&lt;/p&gt;
&lt;p&gt;LLVM 不仅仅使得创建新式语言更加容易，也使得针对现存编程语言进行增强完善更加便捷。它提供了一堆工具使得创造语言的过程中需要的那些令人头疼的事情变得自动化：创建一个编译器，输出代码需要适配多平台和架构，编写代码处理语言当中通常都存在的那些比较晦涩的部分，比如异常。LLVM 的自由开发许可使得它可以被自由的作为一个软件组件或者被部署成服务来使用。&lt;/p&gt;
&lt;p&gt;使用了 LLVM 的编程语言花名册种有很多熟悉的名字。Apple 的 Swift 语言使用 LLVM 作为其编译器框架，Rust 语言使用 LLVM 作为其工具链中的一个核心组件。同样，许多编译器也有其 LLVM 版本，例如 Clang，一个 C/C++ 编译器，它是和 LLVM 功能特别相近的一个项目。还有 Kotlin 语言，其名义上是一门 JVM 语言，也正在开发该语言的一个版本： &lt;a href="https://www.infoworld.com/article/3187370/application-development/kotlin-compiles-directly-to-native-code-via-llvm.html"&gt;Kotlin Native&lt;/a&gt;，其使用 LLVM 编译成机器原生代码。&lt;/p&gt;
&lt;h2 id="llvm-是什么"&gt;LLVM 是什么&lt;/h2&gt;
&lt;p&gt;LLVM 最核心的功能就是，可以通过编码方式创造机器原生代码。一个开发者可以使用其 API 创造指令，该指令是一种 intermediate representation 格式，简称 IR。之后，LLVM 能够将 IR 编译成独立的二进制文件，或者对其执行即时编译以运行在另外一个程序，例如某编程语言的解释器的上下文中。&lt;/p&gt;
&lt;p&gt;很多编程语言中很常见的结构体或者模式，LLVM 的 API 都提供了原始语义的支持。例如，几乎所有的语言都有函数或者全局变量的概念，LLVM 在其 IR 中把函数和全局变量作为独立元素定义出来，因此你不需要花太多时间和精力来重复造这些特定的轮子，你只需要使用 LLVM 的实现，专注于你的语言最需要关注的地方即可。&lt;/p&gt;</description></item><item><title>模块化 Xcode 工程</title><link>https://hechen.github.io/zh-cn/post/modular-xcode-projects/</link><pubDate>Sun, 19 Nov 2017 18:17:15 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/modular-xcode-projects/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://ppinera.es/2017/09/29/modular-xcode-projects.html"&gt;Modular Xcode projects&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &amp;amp; Copyright &lt;a href="http://twitter.com/pepibumur"&gt;@pepibumur&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;翻译：&lt;a href="https://twitter.com/OgreMergO"&gt;@OgreMergO&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 Xcode 构建模块化的工程就需要对工程结构以及其基础概念有很好的理解才行。&lt;/p&gt;
&lt;p&gt;我们平时不怎么关注工程结构本身，只有在工程逐渐变大，要添加更多依赖的时候才会注意的到。而即使到了这个时候，我们大多数的工程都会使用 &lt;a href="https://cocoapods.org/"&gt;CocoaPods&lt;/a&gt; 来设置那些依赖项，或者 &lt;a href="https://github.com/carthage"&gt;Carthage&lt;/a&gt;, 后者虽然没有帮我们做依赖性的设置，但是使得我们会更容易的，通过在工程的 build phase 选项中添加一些内容，达到同样的目的。当配置项越来越复杂，我们就很容易产生困惑，这是因为我们并没有完全掌握 Xcode 工程中所涉及的所有元素。我经常被问到的问题如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我能不能在工程里同时使用 Carthage，Cocoapods 以及自己个人的依赖设置？&lt;/li&gt;
&lt;li&gt;我添加了依赖，但是当模拟器打开 App 的时候 Crash 了。&lt;/li&gt;
&lt;li&gt;为什么我需要只在某些 targets 里嵌入 framework？&lt;/li&gt;
&lt;li&gt;我的 framework 应该是静态的还是动态的？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这篇博文中，我会引导你遍历 Xcode Project 中的各个元素，指导你如何通过改变这些元素来模块化你的设置项。我希望下次你遇到上面这些问题的时候，你不需要再花大量时间取 Stack Overflow 上查这些确定的答案。&lt;/p&gt;
&lt;h3 id="elements-"&gt;Elements ⚒&lt;/h3&gt;
&lt;h4 id="target"&gt;Target&lt;/h4&gt;
&lt;p&gt;工程（Projects）都是由多个更小的叫做 target 的单元组成的。这些 target 包含编译特定平台产品，比如 frameworks, libraries, apps, testing bundles, extensions 等所需要的配置。 你可以在&lt;a href="https://github.com/xcodeswift/xcproj/blob/master/Sources/xcproj/PBXProductType.swift"&gt;这里&lt;/a&gt;看到 target 所有可用的类型。 Target 可以相互依赖，当一个 target A 依赖另外一个 target B 的时候，target B 就会被先构建出来以便 target A 使用其产出。而 target 的配置项会涉及以下几个地方：&lt;/p&gt;</description></item><item><title>在 Swift 中使用工厂模式进行依赖注入</title><link>https://hechen.github.io/zh-cn/post/dependency-injection-using-factories-in-swift/</link><pubDate>Sun, 19 Nov 2017 18:17:15 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/dependency-injection-using-factories-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/dependency-injection-using-factories-in-swift/"&gt;Dependency injection using factories in Swift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;依赖注入是一项使得代码更加可测试的关键工具。我们不需要持有某些对象，或者创建这些对象的自有依赖，或者通过单例来获取他们，而是尽可能使那些对象能够正常运转所必须的一切内容（其他对象）通过外界传入，这样做的好处在于，一方面能清晰的看得到某个对象的所有依赖项，另一方便也使得测试工作变得更为简单（因为我们可以模拟这些依赖项来捕获、验证状态以及值。）&lt;/p&gt;
&lt;p&gt;然而，尽管依赖注入确实很有用，但是当在工程中广泛使用的时候还是会有一些痛点。随着某个对象的依赖项越来越多，初始化该对象就变得越来越蹩脚。虽然使得代码可测没毛病，但是如果像下面这种每次需要这样来写初始化方法，也太不爽了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class UserManager {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; init(dataLoader: DataLoader, database: Database, cache: Cache,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; keychain: Keychain, tokenManager: TokenManager) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以，这周咱们来深入了解一下某种依赖注入的技巧，使得我们的代码不失去可测性，我们也不需要再强迫自己去写一团初始化方法或者复杂的依赖管理的代码。&lt;/p&gt;
&lt;h2 id="传递依赖项"&gt;传递依赖项&lt;/h2&gt;
&lt;p&gt;我们遇到上面代码 demo 中的问题，最主要的原因是我们需要把这么多依赖项传递给某个对象，以便之后来使用。举例来说，我们在构建一个消息收发的 App，这里有一个 view controller 需要展示某个用户所有的消息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class MessageListViewController: UITableViewController {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; private let loader: MessageLoader
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; init(loader: MessageLoader) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; self.loader = loader
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; super.init(nibName: nil, bundle: nil)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; override func viewWillAppear(_ animated: Bool) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; super.viewWillAppear(animated)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; loader.load { [weak self]() messages in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; self?.reloadTableView(with: messages)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上代码能看到，我们给 &lt;code&gt;MessageListViewController&lt;/code&gt; 传入某个依赖项 &lt;code&gt;MessageLoader&lt;/code&gt;，之后其被用来加载数据。这里其实并没有太大的问题，因为仅仅只有一个依赖而已。然而，我们的列表视图并不是一个死气沉沉的展示而已，某些状态下还需要们进行导航到某视图控制器的工作。&lt;/p&gt;</description></item><item><title>处理 Swift 中非可选的可选值类型</title><link>https://hechen.github.io/zh-cn/post/handling-non-optional-optionals-in-swift/</link><pubDate>Tue, 14 Nov 2017 21:53:52 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/handling-non-optional-optionals-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/handling-non-optional-optionals-in-swift/"&gt;Handling non-optional optionals in Swift&lt;/a&gt;
原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可选值（optionals）无可争议的是 &lt;code&gt;swift&lt;/code&gt; 语言中最重要的特性之一，也是和其他语言，例如 &lt;code&gt;Objective-C&lt;/code&gt; 的最大区别。通过强制处理那些有可能出现 &lt;code&gt;nil&lt;/code&gt; 的地方，我们就能写出更有预测性的以及更健壮的代码。&lt;/p&gt;
&lt;p&gt;然而，有些时候可选值可能会致你于尴尬的境地，尤其是你作为开发者了解（甚至是有些猜测的成分在），有的特定变量始终是非空（non-nil）的，即使它是一个可选类型。例如，我们在一个视图控制器中处理视图的时候：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TableViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UITableView&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tableView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UITableView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewModelDidUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;_&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;reloadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这也是对于很多 Swift 程序员争论比较激烈的地方，程度不亚于讨论 tabs 和 spaces 的用法。有的人会说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既然它是一个可选值，你就应该时刻使用 &lt;code&gt;if let&lt;/code&gt; 或者 &lt;code&gt;guard let&lt;/code&gt; 的方式进行解包。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然而另外一些人则采用完全相反，说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既然你知道这个变量在使用的时候不会为 &lt;code&gt;nil&lt;/code&gt;，使用 &lt;code&gt;!&lt;/code&gt; 强制解包多好。崩溃也要比让你的程序处于一个未知状态要好吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本质上来讲，我们这里讨论的是要不要采用防御性编程（&lt;a href="https://en.wikipedia.org/wiki/Defensive_programming"&gt;defensive programming&lt;/a&gt;）的问题。我们是试图让程序从一个未知状态恢复还是简单的放弃，然后让它崩溃掉？&lt;/p&gt;
&lt;p&gt;如果非得让我对这个问题给出一个答案的话，我更倾向于后者。未知状态真的很难追踪 bug，会导致执行很多不想执行的逻辑，采用防御性编程就会使得你的代码很难追踪，出现问题很难追踪。&lt;/p&gt;
&lt;p&gt;但是，我不太喜欢给出一个二选一的答案。相反，我们可以寻找一些技术手法，用更精妙的方式的解决上面提到的问题。&lt;/p&gt;
&lt;h2 id="它真的可选的吗"&gt;它真的可选的吗？&lt;/h2&gt;
&lt;p&gt;那些可选类型的，但是被代码逻辑真实需要的变量和属性，实际上是架构瑕疵的一个体现。如果在某些地方确实需要它，但是它又不在，就会使得你的代码逻辑处于未知状态，那么它就不应该是可选类型的。&lt;/p&gt;
&lt;p&gt;当然，在某些特定场景下，可选值确实很难避免（尤其是和特定的系统 API 交互的时候），那对于大部分这种情况，我们有一些技术来处理从而避免可选值。&lt;/p&gt;
&lt;h3 id="lazy-要比非可选的可选值更好"&gt;lazy 要比非可选的可选值更好&lt;/h3&gt;
&lt;p&gt;某些属性的值需要在其父类创建之后再生成（比如视图控制器中的那些视图，应该在 &lt;code&gt;loadView()&lt;/code&gt;或者 &lt;code&gt;viewDidLoad()&lt;/code&gt;方法中被创建），对于这种属性要避免其可选类型的方法就是使用 &lt;code&gt;lazy&lt;/code&gt; 属性。一个&lt;code&gt;lazy&lt;/code&gt;属性是可以是非可选类型的，同时也不在其父类的初始化方法里被需要，它会在其第一次被获取的时候创建出来。&lt;/p&gt;
&lt;p&gt;让我们改一下上面的代码，使用 lazy 来改造 tableView 属性：&lt;/p&gt;</description></item><item><title>Capturing objects in Swift closures</title><link>https://hechen.github.io/zh-cn/post/capturing-objects-in-swift-closures/</link><pubDate>Sun, 12 Nov 2017 20:56:50 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/capturing-objects-in-swift-closures/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/posts/capturing-objects-in-swift-closures"&gt;Capturing objects in Swift closures&lt;/a&gt;
原作者 &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;自从 Block 在 iOS4 被引入 Objective-C 的世界之后就成为了 Apple 各平台上最时髦的 API 的重要组成部分了。当 Swift 语言出现的时候，blocks 的概念就摇身一变通过 closure 的形式引入，成为了目前我们可能每一天都在用的语言特性之一了。&lt;/p&gt;
&lt;p&gt;Closure 目前已经被我们广泛的使用了，即使如此，我们在使用它的时候还是需要有很多需要注意的点，并且需要做很多额外的操作。这篇文章，我们来近距离的了解 closure，主要是了解其捕获变量的机制以及那些能够更好的让我们来处理变量捕获的技术。&lt;/p&gt;
&lt;h2 id="伟大的-escape"&gt;伟大的 escape&lt;/h2&gt;
&lt;p&gt;Closure 有两种类型：escaping 和 non-escaping。当一个 closure 是 escaping（使用 &lt;code&gt;@escaping&lt;/code&gt; 修饰闭包参数）的，也就意味着其会被以各种形式存储下来（无论是通过 property 还是被其他 closure 捕获）。相反，Non-Escaping 的　closure 意味着其不能被存储，而且在使用它的地方就必须直接被执行。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;译者注： 可以参见 &lt;a href="http://hechen.info/2017/11/12/autoclosure-escape/"&gt;《@autoclosure &amp;amp;&amp;amp; @escape》&lt;/a&gt; 一文。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一个显而易见的例子就是当你在一个集合类型上使用函数式操作的时候，例如 &lt;code&gt;forEach&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[1, 2, 3].forEach { number in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上代码所示，closure 都直接作用于集合的每一个元素上，也就无需把该闭包变为 escaping 的。&lt;/p&gt;
&lt;p&gt;而 escaping 的 closures 最常见的就是在那些异步 API 中，例如 &lt;code&gt;DispatchQueue&lt;/code&gt;。例如，当你异步执行某 closure 的时候，这个 closure 就会 escape。&lt;/p&gt;</description></item><item><title>@autoclosure &amp;&amp; @escape</title><link>https://hechen.github.io/zh-cn/post/autoclosure-escape/</link><pubDate>Sun, 12 Nov 2017 20:01:54 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/autoclosure-escape/</guid><description>&lt;p&gt;我们知道在 &lt;code&gt;swift&lt;/code&gt; 中，闭包（closure）是一等公民，因此可以被当作参数传递，在学习 swift 的过程中经常会看到某些关键字修饰该闭包，&lt;code&gt;@autoclosure&lt;/code&gt;， &lt;code&gt;@escape&lt;/code&gt; 就是其中比较常见的两种关键字。&lt;/p&gt;
&lt;h3 id="escape-和-nonescape"&gt;@escape 和 @nonescape&lt;/h3&gt;
&lt;p&gt;当一个闭包被当作参数传递给一个函数，但是当该函数内容执行完毕返回之后，该闭包才会被执行，我们就称该闭包要 escape 某个函数，那 &lt;code&gt;@escape&lt;/code&gt; 关键字就是用来表示该闭包是允许在函数返回之后被调用的。&lt;/p&gt;
&lt;p&gt;我们用 swift 官方文档的例子来看，如下所示&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;completionHandlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;someFunctionWithEscapingClosure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;escaping&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;completionHandlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;someFunctionWithEscapingClosure(_:)&lt;/code&gt; 的参数是一个闭包，函数内部会把传入的闭包存到之前声明的数组里以便之后进行调用，可以看到，在函数参数的声明部分添加了 &lt;code&gt;@escaping&lt;/code&gt; 关键字，如果这里不添加的话，就会在编译的时候报错：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error: passing non-escaping parameter &lt;span class="s1"&gt;&amp;#39;completionHandler&amp;#39;&lt;/span&gt; to &lt;span class="k"&gt;function&lt;/span&gt; expecting an @escaping closure
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; completionHandlers.append&lt;span class="o"&gt;(&lt;/span&gt;completionHandler&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;针对标记了 @escaping 关键字含义代表你必须在该闭包内部显式的使用 self 关键字，官方文档中又列举了另外一个例子，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;someFunctionWithNonescapingClosure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;closure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;closure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;someFunctionWithEscapingClosure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;someFunctionWithNonescapingClosure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SomeClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Prints &amp;#34;200&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;completionHandlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;first&lt;/span&gt;&lt;span class="p"&gt;?()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Prints &amp;#34;100”&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;someFunctionWithEscapingClosure(_:)&lt;/code&gt; 是一个可逃逸的闭包，意味着你需要显示的调用 self 关键字， 而 &lt;code&gt;someFunctionWithNonescapingClosure(_:)&lt;/code&gt; 是非逃逸的闭包，意味着你可以隐式的调用 self。&lt;/p&gt;</description></item><item><title>关于 iOS10 Notification 的那些事儿</title><link>https://hechen.github.io/zh-cn/post/ios-notification/</link><pubDate>Sat, 20 May 2017 10:10:54 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/ios-notification/</guid><description>&lt;h2 id="概览"&gt;概览&lt;/h2&gt;
&lt;p&gt;推送通知我们大家都不陌生，可以说几乎每个使用智能手机的人每天都会被不同的通知 &lt;em&gt;打扰&lt;/em&gt; 到，正式因为合适的推送是吸引用户注意力的利器，其成为了各 App 吸引用户，将用户带回到 App 本身，提升用户的活跃度的一种必要的方式。&lt;/p&gt;
&lt;p&gt;当然，过度的推送本身则是一件对用户影响特别大的事情，毕竟注意力被打断，因此合适的推送时机也是各个 App 开发者所要注意的，否则他的 App 就会成为用户勿扰名单里的一员了。&lt;/p&gt;
&lt;p&gt;之前刚开始学习 iOS 开发的时候还整理了下当时部署 iOS 远程推送的流程，详见：&lt;a href="http://hechen.info/2015/07/30/iOS-Push-Notification/"&gt;iOS 远端推送部署详解&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;接下来，我们大致回顾一下 iOS 平台关于推送都有哪些历程？&lt;/p&gt;
&lt;h2 id="历史"&gt;历史&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;lt;= iOS 6&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;远程推送通知 （iOS 3）&lt;/li&gt;
&lt;li&gt;本地通知 （iOS 4）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 7 参考&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 Silent Remote Notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 8 参考 &lt;a href="https://developer.apple.com/videos/play/wwdc2014/713/"&gt;WWDC 2014 Session 713&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 Actionable Notifications&lt;/li&gt;
&lt;li&gt;修改 Notification 的权限请求&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 9 参考 &lt;a href="https://developer.apple.com/videos/play/wwdc2015/720/"&gt;WWDC 2015 Session 720&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 Text Input Action&lt;/li&gt;
&lt;li&gt;UIUserNotificationActionBehavior&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 10 参考 &lt;a href="https://developer.apple.com/videos/play/wwdc2016/707"&gt;WWDC 707 Introduction to Notifications&lt;/a&gt; &amp;amp;&amp;amp; &lt;a href="https://developer.apple.com/videos/play/wwdc2016/708"&gt;WWDC 708 Advanced Notifications&lt;/a&gt;&lt;/p&gt;</description></item><item><title>如何使用UIBezierPath实现一个弹性视图动画</title><link>https://hechen.github.io/zh-cn/post/elastic-view-animation-using-uibezierpath/</link><pubDate>Wed, 02 Dec 2015 18:53:51 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/elastic-view-animation-using-uibezierpath/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://hackernoon.com/elastic-view-animation-or-how-i-built-dgelasticpulltorefresh-269a3ba8636e"&gt;Elastic view animation, or how I built DGElasticPullToRefresh&lt;/a&gt;
原作者 &lt;a href="https://twitter.com/gontovnik"&gt;@gontovnik&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/gontovnik/DGElasticPullToRefresh"&gt;DGElasticPullToRefresh&lt;/a&gt; 展示了如何实现一个弹性效果。效果如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="DGElasticPullToRefresh.gif" loading="lazy" src="https://i.imgur.com/NzdoFS9.gif"&gt;&lt;/p&gt;
&lt;p&gt;开发环境：
Xcode 7
Swift 2.0&lt;/p&gt;
&lt;p&gt;要求：
开发者至少对 UIBezierPath 和 UIGestureRecognizer 有一定的了解.&lt;/p&gt;
&lt;h3 id="理解业务逻辑"&gt;理解业务逻辑&lt;/h3&gt;
&lt;p&gt;你可能从上面的效果图上可以看到一些端倪，这个动画中我们主要使用到了UIBezierPath来实现上面这种效果。
我们首先创建一个贝塞尔曲线路径的CAShapeLayer，然后当你的手指在屏幕上移动的时候我们将移动所有的控制点来呈现动画。每一个控制点会使用一个可见的UIView来表示。下面有几张图来演示它们是如何工作的，我将所有的控制点标识成了红色：&lt;/p&gt;
&lt;p&gt;&lt;img alt="1_vAmk8ulHh4OJji_X35zW4g" loading="lazy" src="media/1_vAmk8ulHh4OJji_X35zW4g.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="1_ru8KwJ6DZWXgJm1tm_Sw_Q" loading="lazy" src="media/1_ru8KwJ6DZWXgJm1tm_Sw_Q.png"&gt;&lt;/p&gt;
&lt;p&gt;第二章图片中将每个表示控制点的 View 的变量名称标记出来了，如L3，L2等。&lt;/p&gt;
&lt;p&gt;当你的手指释放的时候，我们就播放Spring动画，让所有的控制点向其初始位置以一定的回弹效果移动。当所有的视图在动画播放过程中，我们需要时刻计算并我们的贝塞尔曲线（每一帧都进行计算）。因此，我们准备使用CADisplayLink，CADisplayLink在主循环中运行而且每一帧都会去执行指定的方法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display. - Apple doc&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;让我们开始Code吧~&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="build"&gt;Build&lt;/h3&gt;
&lt;p&gt;创建一个单视图控制器，然后将下面这些代码粘贴到viewController.swift文件的类声明中。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: Vars
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;private let minimalHeight: CGFloat = 50.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;private let shapeLayer = CAShapeLayer()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;override func loadView() {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; super.loadView()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shapeLayer.frame = CGRect(x: 0.0, y: 0.0, width: view.bounds.width, height: minimalHeight)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shapeLayer.backgroundColor = UIColor(red: 57/255.0, green: 67/255.0, blue: 89/255.0, alpha: 1.0).CGColor
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; view.layer.addSublayer(shapeLayer)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: &amp;#34;panGestureDidMove:&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: Methods
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;func panGestureDidMove(gesture: UIPanGestureRecognizer) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if gesture.state == .Ended || gesture.state == .Failed || gesture.state == .Cancelled {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; } else {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shapeLayer.frame.size.height = minimalHeight + max(gesture.translationInView(view).y, 0)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;override func preferredStatusBarStyle() -&amp;gt; UIStatusBarStyle {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return .LightContent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上代码中，我们做的工作主要有以下几点：&lt;/p&gt;</description></item><item><title>Objective-C中Category的一点东西</title><link>https://hechen.github.io/zh-cn/post/objective-c%E4%B8%ADcategory%E7%9A%84%E4%B8%80%E7%82%B9%E4%B8%9C%E8%A5%BF/</link><pubDate>Mon, 14 Sep 2015 15:36:07 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/objective-c%E4%B8%ADcategory%E7%9A%84%E4%B8%80%E7%82%B9%E4%B8%9C%E8%A5%BF/</guid><description>&lt;p&gt;Objective-C中的分类（category）是一种编译时的手段，其允许我们通过给某个已知类添加方法来扩充该类的一种方式。当然这其中是有限制的，就是不能给已知类添加新的实例变量。&lt;/p&gt;
&lt;p&gt;如下代码展示：&lt;/p&gt;
&lt;p&gt;类&lt;code&gt;MyClass&lt;/code&gt;为一个简单的类，其中有实例方法 &lt;code&gt;-print&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;lt;Foundation/Foundation.h&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; : &lt;span class="nc"&gt;NSObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#import &amp;#34;MyClass.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;@implementation MyClass
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- (void)print
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; NSLog(@&amp;#34;MyClass...&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;@end
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们向为MyClass再添加一个方法Hello，那我们就可以用分类的方法实现，我们为其添加分类MyAddition1和MyAddition2，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;#34;MyClass.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="nl"&gt;(MyAddition1)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nonatomic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;#34;MyClass+MyAddition1.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@implementation&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="nl"&gt;(MyAddition1)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@&amp;#34;hello!!!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中MyAddition为分类的名称，而文件名字约定俗成用“类名+扩展名”的形式。这样，我们就能够为MyClass类添加hello方法了，如下调用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;#34;MyClass+MyAddition1.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt; &lt;span class="n"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Category 的使用场景主要有以下几种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;需求变更，需要为已知类添加方法；&lt;/li&gt;
&lt;li&gt;将类的不同模块实现划分: a)具体可以将类的实现分开到不同的文件里面； b)将不同功能的分类文件交由不同的开发者实现；&lt;/li&gt;
&lt;li&gt;想为Apple基础类库添加自己需要的方法，实际上和1相似；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是分类的使用目前也有需要注意的地方：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Category可以访问原有类的实例变量，但不能添加实例变量；&lt;/li&gt;
&lt;li&gt;Category中实现和原有类中相同签名的方法时，会覆盖原有类的方法；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是这两点均可以通过其他方式实现：&lt;/p&gt;
&lt;h3 id="为分类添加实例变量"&gt;为分类添加实例变量&lt;/h3&gt;
&lt;p&gt;为分类添加实例变量主要通过关联对象的方法。如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;#34;MyClass.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="nl"&gt;(MyAddition1)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nonatomic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;#34;MyClass+MyAddition1.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#import &amp;lt;objc/objc-runtime.h&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@implementation&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="nl"&gt;(MyAddition1)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;setName:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;objc_setAssociatedObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OBJC_ASSOCIATION_COPY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;objc_getAssociatedObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="如果调用被分类覆盖掉的原方法"&gt;如果调用被分类覆盖掉的原方法&lt;/h4&gt;
&lt;p&gt;实际上，通过查看Objc runtime源码可以获知，分类方法并不是绝对意义上的覆盖原有类的方法，只是在调用的时候调用顺序导致而已，这涉及到Objc runtime的一些内容。&lt;/p&gt;</description></item><item><title>理解Objective-C运行时</title><link>https://hechen.github.io/zh-cn/post/understanding-the-objective-c-runtime/</link><pubDate>Mon, 07 Sep 2015 17:55:33 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/understanding-the-objective-c-runtime/</guid><description>&lt;p&gt;Objective-C 运行时对于刚刚踏入 Cocoa/Objective 世界的人是很容易忽
略的 Objective-C 语言的特性之一。原因就是尽管 Objective-C 是一门几个小时之内入门的语言，但是投身 Cocoa 的新手们会花费大量时间在 Cocoa 框架中，试图搞清楚他到底是怎么工作的。 我觉得每个开发者都应该对其有深入的了解，明白一些内部的实现细节，而不仅仅只知道代码 &lt;code&gt;[target doMethodWith:var]&lt;/code&gt; 会被编译器转换成 &lt;code&gt;objc_msgSend(target,@selector(doMethodWith:),var1);&lt;/code&gt; 而已。了解 Objective-C 运行时的原理有助于你对 Objective-C 语言有更深入的理解，清楚你得 App 是怎么运行的。我觉得这对无论是 Mac/iPhone 新手或者老手都会有所帮助。&lt;/p&gt;
&lt;p&gt;[TOC]&lt;/p&gt;
&lt;h4 id="objective-c运行时是开源的"&gt;Objective-C运行时是开源的&lt;/h4&gt;
&lt;p&gt;Objective-C 运行时是开源的，你可以随时从 &lt;a href="http://opensource.apple.com"&gt;Apple&lt;/a&gt; 获取到。实际上查看 Objective-C 运行时源码是我搞清楚这个语言是怎么运作的首选方法，而不是去查看和它相关的苹果文档。你可以到&lt;a href="http://opensource.apple.com/tarballs/objc4/objc4-437.1.tar.gz"&gt;这里&lt;/a&gt; 下载到运行时的源码（截止到译者翻译的时候，最新版本的文件是objc4-647.tar.gz）。&lt;/p&gt;
&lt;h4 id="动态-vs-静态"&gt;动态 Vs. 静态&lt;/h4&gt;
&lt;p&gt;Objective-C 是一门动态的面向对象语言，这意味着它可以将编译链接时决定的事情推迟到运行时进行。这就给了你很大的灵活性，你可以按照自己的需要重定向消息到适当的对象上，你甚至可以交换方法实现（译者注：method swizzling，方法调配，开发者常用此技术向原有实现中添加新功能）。而运行时可以使对象明白自己可以响应哪些消息，不能响应哪些消息（译者注：introspect 内省），并正确的派发消息。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;译者注： 内省（introspection）是面向对象语言和环境的一个强大特性，Objective-C和Cocoa在这个方面尤其的丰富。内省是对象揭示自己作为一个运行时对象的详细信息的一种能力。这些详细信息包括对象在继承树上的位置，对象是否遵循特定的协议，以及是否可以响应特定的消息。NSObject协议和类定义了很多内省方法，用于查询运行时信息，以便根据对象的特征进行识别。&lt;/p&gt;
&lt;p&gt;参考: &lt;a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Introspection/Introspection.html"&gt;Objective-C 的 Introspection&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们将这些特征和C语言进行对比来看，在C中，你从&lt;code&gt;main()&lt;/code&gt;函数开始，然后按顺序从上往下写你的代码逻辑或者执行函数方法。C结构体是无法将请求转发到其他目标对象来执行方法的。你很可能写了如下类似的代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt; stdio.h &amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编译器解析、优化然后将优化后的代码转换成如下的汇编语言：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;align&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x90&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;globl&lt;/span&gt; &lt;span class="n"&gt;_main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;_main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;Leh_func_begin1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pushq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;Llabel1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;Llabel2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subq&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;Llabel3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rax&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movl&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;edi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;ecx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movl&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;ecx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;xorb&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;al&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;leaq&lt;/span&gt; &lt;span class="n"&gt;LC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rip&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rcx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rdi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;_printf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movl&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;movl&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;eax&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;addq&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;popq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;Leh_func_end1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cstring&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;LC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asciz&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后将其和C库链接生成可执行文件。相比之下，Objective-C语言整个过程和上面类似，不过代码的产生依赖Objective-C运行时的具体表现（译者注：运行时按照不同情况，生成不同的代码吧）。当我们刚接触Objective-C语言的时候，我们可能被告知像如下代码&lt;/p&gt;</description></item><item><title>我们是如何创建iOS版的Guillotine菜单动画的</title><link>https://hechen.github.io/zh-cn/post/how-we-created-guillotine-menu-animation-for-ios/</link><pubDate>Tue, 01 Sep 2015 15:52:07 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/how-we-created-guillotine-menu-animation-for-ios/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://yalantis.com/blog/how-we-created-guillotine-menu-animation/"&gt;How We Created Guillotine Menu Animation for iOS&lt;/a&gt;
原作者 &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你是否曾经有过这样的疑问？为什么app中几乎是清一色的&lt;strong&gt;侧&lt;/strong&gt;边栏（sidebar），为什么不把它做成&lt;strong&gt;top&lt;/strong&gt;Bar或者&lt;strong&gt;bottom&lt;/strong&gt;Bar，甚至&lt;strong&gt;corner&lt;/strong&gt;Bar呢？&lt;/p&gt;
&lt;p&gt;本文将要谈到的就是当前导航条动画的一个新趋势。&lt;/p&gt;
&lt;p&gt;动画很有趣，但更重要的是能发挥很大的作用，它们可以改变你思考问题的方式，使得你的产品更好用并提升的app整体的用户体验。接下来我们将要展示的是设计师&lt;strong&gt;Vitaly Rubtsov&lt;/strong&gt;的一个非常棒的点子：&lt;/p&gt;
&lt;p&gt;“每个设计师都有那么一刻感到无聊。因为几乎所有（对动画）的完善修补、裁剪以及规格设定都给他们留下了很少发挥想象的余地。而每当这些时候，我就会驱使自己打开Adobe After Effects软件然后创作一些比较有趣的东西。&lt;/p&gt;
&lt;p&gt;当我开始想我要创建一个什么东西的时候，我突然有个想法，通常侧边栏都从屏幕左侧划出，同时将所有的内容都移动到右侧位置。这种传统侧边栏的实现方式太过无聊了。那如果我们将侧边栏变成上边栏会怎么样呢？它从页面的上方掉落然后以一种特别的方式呈现，听起来很棒不是吗？“&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vitaly&lt;/strong&gt;设计的topBar动画由我们的iOS开发工程师&lt;strong&gt;Maksym Lazebnyi&lt;/strong&gt;使用&lt;code&gt;swift&lt;/code&gt;语言实现，并且开发者给了它一个很有趣的名字—— &lt;strong&gt;Guillotine Menu&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Guillotine Menu Animation" loading="lazy" src="https://i.imgur.com/Q2GueMC.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="我们是如何开发guillotine-menu的"&gt;我们是如何开发Guillotine Menu的？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;by Makssym Lazebnyi&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实际上，我们的iOS团队见到过很多实现这种动画效果的方法。我们选择了其中一种方式实现，这种方式允许开发者在Storyboard中以任何方式自定义菜单。&lt;/p&gt;
&lt;p&gt;为了实现我们的转场动画（transitioning animation），我们创建了一个UIStoryboardSegue的子类和一个自定义动画管理器（custom animation controller）。基本上这就是你实现该动画所需要做的全部工作，除非你想让它更炫酷。当然我们也确实想这样做，因此还创建了一些辅助的类。&lt;/p&gt;
&lt;p&gt;整体上，你需要三个类以及一个&lt;code&gt;UIView&lt;/code&gt;的扩展类来创建这个动画，如下所示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GuillotineMenuSegue&lt;/code&gt;. 该类是继承自&lt;code&gt;UIStoryboardSegue&lt;/code&gt;类。我们使用它来模态显示菜单，并实现由 &lt;code&gt;GuillotineMenuTransitionAnimation&lt;/code&gt;类控制的呈现动画。 &lt;code&gt;GuillotineMenuSegue&lt;/code&gt;允许你为菜单添加透明度，当然本文并没有做这个工作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GuillotineMenuTransitionAnimation&lt;/code&gt;. 该类是为了自定义呈现 &lt;code&gt;GuillotineMenuViewController&lt;/code&gt; 类中视图的动画所用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GuillotineMenuViewController&lt;/code&gt;. 该类是一个UIViewController的子类，存放菜单视图所用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除此之外，我们还为UIView添加了扩展方法以便能为子视图添加约束来更好的适应父视图。&lt;/p&gt;
&lt;p&gt;接下来，我们逐一对每一个类进行阐述。&lt;/p&gt;
&lt;h4 id="guillotinemenusegue"&gt;GuillotineMenuSegue&lt;/h4&gt;
&lt;p&gt;这个类中没有什么特别的地方，我只提及一些关键点。&lt;/p&gt;
&lt;p&gt;在该类重载的init方法中，我们检查目标视图控制器（destination view controller）是否遵守&lt;code&gt;GuillotineAnimationProtocol&lt;/code&gt; 协议（该协议我们后面会讲到）。在重载的perform方法中我们将self设置成一个过渡动画的代理。&lt;/p&gt;
&lt;p&gt;在代理方法 &lt;code&gt;animationControllerForPresentedController&lt;/code&gt; 中我们使用关联对象将&lt;code&gt;GuillotineMenuSegue&lt;/code&gt;的实例对象（&lt;strong&gt;self&lt;/strong&gt;）关联到具体的将要呈现的视图控制器中，这样当menu view controller呈现在屏幕上的时候，segue实例不会被销毁。（译者注：代码如下）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objectivec" data-lang="objectivec"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UIViewControllerAnimatedTransitioning&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;animationControllerForPresentedController:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;presented&lt;/span&gt; &lt;span class="nf"&gt;presentingController:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;presenting&lt;/span&gt; &lt;span class="nf"&gt;sourceController:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 将segue示例self关联到将要呈现的试图控制器presented中，这样确保presented生命周期内segue实例不会被释放
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;objc_setAssociatedObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;presented&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OBJC_ASSOCIATION_RETAIN_NONATOMIC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;GuillotineTransitionAnimation&lt;/span&gt; &lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nl"&gt;initWithMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;AnimationModePresentation&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="guillotinemenutransitionanimation"&gt;GuillotineMenuTransitionAnimation&lt;/h4&gt;
&lt;p&gt;所有的动画处理逻辑都在这个类中实现。&lt;/p&gt;
&lt;p&gt;起初我们考虑使用 &lt;code&gt;animateWithDuration usingSpringWithDamping &amp;amp; initialSprintVelocity&lt;/code&gt;方法，但是当我们认真考虑这个动画之后我们改变了主意。我们需要实现的菜单动画中，当菜单从上方掉落之后，需要和父视图的左侧边界发生碰撞，产生碰撞效果。而上面方法中的回弹效果会穿过左边界（spring through the border of the superview），因此我们放弃了这个实现方式，转而采用了&lt;code&gt;UIDynamicAnimator&lt;/code&gt;。&lt;/p&gt;</description></item></channel></rss>