四月月度总结大会

整体而言,这月份算是历经风波的一个月份,心中有着很沉重的感觉,然而焦虑急躁却又同时伴随着。这段日子和他人产生摩擦而有纷争,尽量控制自己的脾气,以避免伤了和气!

有关事业工作方面的话:接踵而至的事项太多了,自己又有很多酝酿已久的想法和计划,缓慢脚步中时而被催促,略显无奈的敷衍过去,又要配合东配合西的,真是使人感到烦透了。本身很坚持的点,如今都要配合别人,妥协的滋味却必须吞不下去,心里觉得百般不愿意。

个人问题:抱憾原本的机缘过去了和对方是互相喜欢的,怎无奈就会感到很多牵制,要配合对方使得积怨产生,时常想要爆发开来,或者感情就这样越来越淡了。

想太多而要配合天时地利人和,是有困难的,会因为这样延迟而拖过去了,遇到沟通的机会好好把握,一小步一小步的慢慢来。收入方面有一定数量了,那么用钱方面在做些规划,省下些钱和基金都没问题的。


为 iOS7重新设计App

在 WWDC 上看到 iOS 7 系统的发布后,我们重新审视了自己的应用 Grocery List,并且意识到:iOS 7 对于开发者来说是一个全新的开始,就像七年前 iPhone 首次发布一样。现在仅仅简单地改变设计是不够的,我们不得不重新思考并重构整个 app,从而让它适合 iOS 7 全新的环境。我们也的确是这么做的。

根据用户的反馈和我们自己的使用情况,我们意识到,虽然不能改变 app 基本的操作,但是应该对软件的操作流程进行一些优化。比如在旧版本中,添加产品的数量和单位是一个多步骤的操作过程,需要在多个 controller 之间进行导航。在 Grocery List 2 中,用户不用离开当前屏幕就能在恰当的位置设置数值。

在实现这个目标的过程中,遇到了一些我们觉得值得分享的问题。我们将会从动画和手势开始讲起,然后是界面、色彩以及字体等问题。接下来,为了吸引用户打开 app,我们将不得不思考如何针对 iOS 7 重新设计 app 的图标。最后,我们将分享在我们看来,苹果这次更新的意义何在。

动画

现在随着更新换代,移动设备的性能正变得越来越强大。与此同时,由于可以实时计算物品的物理属性,动画效果也变得愈加真实。在 iOS 7 中,我们不需要在界面中使用阴影和渐变这些效果了,而是应该更关注用户的感觉、手势以及交互的影响。凭借 iOS 7,你可以创建一个新世界而不是模仿旧有的世界。

新 SDK 可以让你简单地创建并使用自定义的动画效果。在 iOS 7 之前,开发者需要做大量额外的工作才能改变 view controller 之间的转场效果。iOS 7 可以让你简单地添加自己的动画,以帮助用户在不同屏幕之间切换的同时还不会丢失关注焦点。

Grocery List 中,我们在显示一个 modal view controller 时使用了轻度定制的转场动画。但是大部分的动画和转场效果都是使用系统默认的。我们本可以使用新的 API 来给 Grocery List 2 添加更多自定义的动画,但其实苹果提供的默认转场动画对于大多数情况来说已经是个不错的解决方案了,这也是为什么我们 app 中的不同 view controller 之间的转场效果和苹果官方的 app 是一样的。正如前边提到的那样,我们 app 的部分操作流程已经明显地发生了改变。所以,我们将会使用自定义动画从而更好地保持用户的关注焦点。

Comparison of the Grocery List and the default view controller push

大多数用户对于 iOS 7 上默认的动画的感觉,是既新鲜又自然,你不需要做很多工作就可以使用这些动画来取悦用户。但是在合适的地方添加一些自定义动画将会提高整个 app 的用户体验。只是请小心不要使用过度。

手势

在拥有了数年的触屏设备的经验后,苹果发现大量使用手势对于用户来说正变得愈加自然。所以在 iOS 7 中,对手势的广泛使用比以前有了更多的可能性。比如在 table view cell 上滑动来显示隐藏的菜单,或者从左向右滑动来返回先前的 view controller,这些手势操作我们已经非常熟悉了,以至于如果一款应用不支持这些手势的话,我们马上就会非常想念它们。在合适的地方,这些直接的手势操作可以帮助用户更高效地完成任务而不会失去关注焦点。

Grocery List 中,我们并没有使用任何自定义手势,但是为了在下个版本中改进用户的操作流程,我们支持在 cell 上进行左右两个方向的滑动来分别展示产品的不同选项。你可以简单地从屏幕的右边缘向左滑动来快速访问菜单以进行列表或者模版的相关设置,而不用在导航栏一层层地返回。

Grocery List 2 gestures

按钮和链接对于用户来说是可见的,也是可识别的,但手势不是。如果你打算用手势来实现某个功能,很好!但是如果 app 中那些依赖手势的功能没有一个等效的可见的控件,那要为用户提供一个好的方法来发现这些手势。一个好的用户界面通常应当是不言自明的。如果你需要一个类似使用说明一样的界面或者视频来描述 app 中的基本功能,那这里面很可能就有问题了。

界面

Read on →

深入理解CocoaPods

CocoaPods 是开发 OS X 和 iOS 应用程序的一个第三方库的依赖管理工具。利用 CocoaPods,可以定义自己的依赖关系 (称作 pods),并且随着时间的变化,以及在整个开发环境中对第三方库的版本管理非常方便。

CocoaPods 背后的理念主要体现在两个方面。首先,在工程中引入第三方代码会涉及到许多内容。针对 Objective-C 初级开发者来说,工程文件的配置会让人很沮丧。在配置 build phases 和 linker flags 过程中,会引起许多人为因素的错误。CocoaPods 简化了这一切,它能够自动配置编译选项。

其次,通过 CocoaPods,可以很方便的查找到新的第三方库。当然,这并不是说你可以简单的将别人提供的库拿来拼凑成一个应用程序。它的真正作用是让你能够找到真正好用的库,以此来缩短我们的开发周期和提升软件的质量。

本文中,我们将通过分析 pod 安装 (pod install) 的过程,一步一步揭示 CocoaPods 背后的技术。

核心组件

CocoaPods是用 Ruby 写的,并由若干个 Ruby 包 (gems) 构成的。在解析整合过程中,最重要的几个 gems 分别是: CocoaPods/CocoaPods, CocoaPods/Core, 和 CocoaPods/Xcodeproj (是的,CocoaPods 是一个依赖管理工具 – 利用依赖管理进行构建的!)。

编者注 CocoaPods 是一个 objc 的依赖管理工具,而其本身是利用 ruby 的依赖管理 gem 进行构建的


CocoaPods/CocoaPod

这是是一个面向用户的组件,每当执行一个 pod 命令时,这个组件都将被激活。该组件包括了所有使用 CocoaPods 涉及到的功能,并且还能通过调用所有其它的 gems 来执行任务。

CocoaPods/Core

Core 组件提供支持与 CocoaPods 相关文件的处理,文件主要是 Podfile 和 podspecs。

Podfile

Podfile 是一个文件,用于定义项目锁需要使用的第三方库。该文件支持高度定制,你可以根据个人喜好对其做出定制。更多相关信息,请查阅 Podfile 指南

Podspec

.podspec 也是一个文件,该文件描述了一个库是怎样被添加到工程中的。它支持的功能有:列出源文件、framework、编译选项和某个库所需要的依赖等。

CocoaPods/Xcodeproj

Read on →

Mac可执行文件

我们用 Xcode 构建一个程序的过程中,会把源文件 (.m.h) 文件转换为一个可执行文件。这个可执行文件中包含的字节码会将被 CPU (iOS 设备中的 ARM 处理器或 Mac 上的 Intel 处理器) 执行。

本文将介绍一下上面的过程中编译器都做了些什么,同时深入看看可执行文件内部是怎样的。实际上里面的东西要比我们第一眼看到的多得多。

这里我们把 Xcode 放一边,将使用命令行工具 (command-line tools)。当我们用 Xcode 构建一个程序时,Xcode 只是简单的调用了一系列的工具而已。Florian 对工具调用是如何工作的做了更详细的讨论。本文我们就直接调用这些工具,并看看它们都做了些什么。

真心希望本文能帮助你更好的理解 iOS 或 OS X 中的一个可执行文件 (也叫做 Mach-O executable) 是如何执行,以及怎样组装起来的。

xcrun

先来看一些基础性的东西:这里会大量使用一个名为 xcrun 的命令行工具。看起来可能会有点奇怪,不过它非常的出色。这个小工具用来调用别的一些工具。原先,我们在终端执行如下命令:

% clang -v

现在我们用下面的命令代替:

% xcrun clang -v

在这里 xcrun 做的是定位到 clang,并执行它,附带输入 clang 后面的参数。

我们为什么要这样做呢?看起来没有什么意义。不过 xcode 允许我们: (1) 使用多个版本的 Xcode,以及使用某个特定 Xcode 版本中的工具。(2) 针对某个特定的 SDK (software development kit) 使用不同的工具。如果你有 Xcode 4.5 和 Xcode 5,通过 xcode-selectxcrun 可以选择使用 Xcode 5 中 iOS SDK 的工具,或者 Xcode 4.5 中的 OS X 工具。在许多其它平台中,这是不可能做到的。查阅 xcrunxcode-select 的主页内容可以了解到详细内容。不用安装 Command Line Tools,就能使用命令行中的开发者工具。

不使用 IDE 的 Hello World

回到终端 (Terminal),创建一个包含一个 C 文件的文件夹:

% mkdir ~/Desktop/objcio-command-line
% cd !$
% touch helloworld.c

接着使用你喜欢的文本编辑器来编辑这个文件 – 例如 TextEdit.app:

% open -e helloworld.c

输入如下代码:

1
2
3
4
5
6
#include <stdio.h>
int main(int argc, char *argv[])
{
    printf("Hello World!\n");
    return 0;
}

保存并返回到终端,然后运行如下命令:

% xcrun clang helloworld.c
% ./a.out
Read on →

Http,Tcp,IP协议

当 app 和服务器进行通信的时候,大多数情况下,都是采用 HTTP 协议。HTTP 最初是为 web 浏览器而定制的,如果在浏览器里输入 http://www.objc.io ,浏览器会通过 HTTP 协议和 www.objc.io 所对应的服务器进行通信。

HTTP是运行在应用层上的应用协议,而不同的层级上都有相应的协议在运行。层级的堆栈关系一般可以这么描述:

Application Layer -- e.g. HTTP
----
Transport Layer -- e.g. TCP
----
Internet Layer -- e.g. IP
----
Link Layer -- e.g. IEEE 802.2

所谓的 OSI(Open Systems Interconnection,开放式系统互联)模型定义了七层结构。本文会关注应用层 (application layer)、传输层 (transport layer) 和网络层 (internet layer),它们分别代表了典型的 HTTP 的应用的 HTTP,TCP 以及 IP。在 IP 之下的是数据连接和物理层级,比如像 Ethernet 的实现之类的东西(Ethernet 拥有一个数据连接部分以及一个物理部分)。

如上文所述,我们只关注应用层,传输层和互联网层的部分,更确切的说,着重探讨一种特殊的混合模式:基于 IP 的 TCP,以及基于 TCP 实现的 HTTP。这就是我们每天使用的 app 的基本网络配置。

通过本文,希望大家能够对HTTP工作原理有一个细致的了解,知道一些常见的 HTTP 问题的产生原因,从而能在实践中尽量避免这些问题的发生。

其实在互联网上传递数据的方式并不只 HTTP 一种。HTTP 之所以被广泛使用的原因是其非常稳定、易用,即便是防火墙一般也是允许 HTTP 协议穿透的。

接下来我们从最低的一层谈起,说说 IP 网络协议。

IP网络协议 (IP-Internet Proctocol)

TCP/IP 中的 IP 是网络协议 (Internet Protocol) 的缩写。从字面意思便知,它是互联网众多协议的基础。

IP 实现了分组交换网络。在协议下,机器被叫做 主机 (host),IP 协议明确了 host 之间的资料包(数据包)的传输方式。

所谓数据包是指一段二进制数据,其中包含了发送源主机和目标主机的信息。IP 网络负责源主机与目标主机之间的数据包传输。IP 协议的特点是 best effort(尽力服务,其目标是提供有效服务并尽力传输)。这意味着,在传输过程中,数据包可能会丢失,也有可能被重复传送导致目标主机收到多个同样的数据包。

IP 网络中的主机都配有自己的地址,被称为 IP 地址。每个数据包中都包含了源主机和目标主机的 IP 地址。IP 协议负责路径计算,即 IP 数据包在网络中的传输传输时,数据包所经过的每一个主机节点都会读取数据包中的目标主机地址信息,以便选择朝什么地方传送数据包。

今天,绝大多数的数据包仍旧是 IPv4(Internet Protocol version 4 网际协议版本 4)的,每一个 IPv4 地址是长度为 32 位。常见采用 dotted-decimal(点分十进制)表示法,具体形式如:198.51.100.42。

新的 IPv6 标准也正在逐渐推广中。它有更大的地址空间:长度为 128 位,这使得数据包在网络中传输时的寻址更容易一些。另外,由于有更多的地址可以分配,诸如网络地址转换等问题也迎刃而解。IPv6 的表示形式为:八组十六进制数以冒号分割,比如:2001:0db8:85a3:0042:1000:8a2e:0370:7334。

IP Hearder

Read on →

Foundation基础集合类

NSArray, NSSet, NSOrderedSet 和 NSDictionary

基础集合类是每一个 Mac/iOS 应用的基本组成部分。在本文中,我们将对”老类” (NSArray, NSSet)和”新类” (NSMapTable, NSHashTable, NSPointerArray) 进行一个深入的研究,探索每一个的效率细节,并讨论其使用场景。

作者提示:本文包含一些参照结果,但它们并不意味着绝对精确,也没有进行均差分析及多次的测试。这些结果的目的是给出运行时统计,来帮助我们认识到通常来说用什么会更快。所有的测试基于 iPhone 5s,使用 Xcode 5.1b1 和 iOS 7.1b1 的 64 位程序。编译选项设置为 -Ofast 的发布构建。Vectorize loops 和 unroll loops (默认设置) 均设置为关闭。

大 O 符号,算法复杂度计量

首先,我们需要一些理论知识。效率通常用大 O 符号描述。它定义了一个函数的极限特征,通常被用于描绘其算法效率。O 定义了函数增长率的上限。不同量级的差异非常巨大,可以看看通常使用的 O 符号的量级以及它们所对应需要的操作数的关系。

例如,如果用算法复杂度为 O(n^2)的算法对一个有 50 个元素的数组排序,需要 2,500 步的操作。而且,还有内部的系统开销和方法调用 — 所以是 250 0个操作的时间常量。 O(1)是理想的复杂度,代表着恒定的时间。好的排序算法通常需要 O(n*log n) 的时间

可变性

大多数的集合类存在两个版本:可变和不可变(默认)。这和其他大多数的框架有非常大的不同,一开始会让人觉得有一点奇怪。然而其他的框架现在也应用了这一特性:就在几个月前,.NET公布了作为官方扩展的不可变集合

最大的好处是什么?线程安全。不可变的集合完全是线程安全的,可以同时在多个线程中迭代,避免各种转变时出现异常的风险。你的 API 绝不应该暴露一个可变的集合。

当然从不可变到可变然后再回来是会有一定代价的 — 对象必须被拷贝两次,所有集合内的对象将被 retain/release。有时在内部使用一个可变的集合,而在访问时返回一个不可变的对象副本会更高效。

与其他框架不同的是,苹果没有提供一个线程安全的可变集合,NSCache 是例外,但它真的算不上是集合类,因为它不是一个通用的容器。大多数时候,你不会需要在集合层级的同步特性。想象一段代码,作用是检查字典中一个 key 是否存在,并根据检查结果决定设置一个新的 key 或者返回某些值 — 你通常需要把多个操作归类,这时线程安全的可变集合并不能对你有所帮助。

其实也有一些同步的,线程安全的可以使用的可变集合案例,它们往往只需要用几行代码,通过子类和组合的方法建立,比如这个 NSDictionary 或这个 NSArray

需要注意的是,一些较新的集合类,如 NSHashTableNSMapTableNSPointerArray 默认就是可变的,它们并没有对应的不可变的类。它们用于类的内部使用,你基本应该不会能找到需要它们的不可变版本的应用场景。

NSArray

NSArray 作为一个存储对象的有序集合,可能是被使用最多的集合类。这也是为什么它有自己的比原来的 [NSArray arrayWithObjects:..., nil] 简短得多的快速语法糖符号 @[...]NSArray 实现了 objectAtIndexedSubscript:,因为我们可以使用类 C 的语法 array[0] 来代替原来的 [array objectAtIndex:0]

性能特征

Read on →

我不会写诗

好想在此写诗一回,
但我始终不敢下笔,
或许是诗不若景美,
或许是诗不及影魅,
抑或是我的诗,
比此刻风景甚悲,
忘记了天空的深邃,
忘记了风的轻微,
愿一只纸鸳的甜蜜,
放飞漫天的心醉。
好想在此放声歌唱,
歌唱那满地的青黄,
那白衣的姑娘,
那微微的碎影,
折射出太阳的方向,
也许那甜蜜的不是淡淡阳光,
而是那挂着笑容的脸庞,
和那美好的希望。

iOS开发者的安卓初步窥探

随着移动软件工业的发展,一个移动产品只局限于 iOS 系统变得越来越不切实际。 Android 目前占有近 80% 的智能手机份额[^1],它能给一个产品带来的潜在用户量实在不能再被忽略了。

在本文中,我会在 iOS 的开发范围内介绍 Android 开发的核心内容。 Android 和 iOS 处理类似的问题集,但在大部分问题上,它们都有不同的解决方式。通过本文,我会使用一个配套项目(在 GitHub 上)来说明如何在两个平台上开发以完成相同的任务。

除了 iOS 开发的相关知识,我假设你在 Java 上也有一定经验,能够安装和使用ADT(Android Development Tools)。此外,如果你最近才开始 Android 开发,读一遍 Google 编写的关于创建你的第一个应用的教程会很有帮助。

UI设计概要

本文不会深入到介绍 iOS 和 Android 在用户体验和设计模式上的不同。然而,了解一些当今 Android 上使用的关键 UI 范式,比如 Action Bar、Overflow Menu、Back Button、Share Action 等,还是会很有好处的。如果你正在认真考虑 Android 开发,我推荐你从 Google Play Store 买个 Nexus 5,将它作为你的主要设备,用满一周,强迫自己最大程度的去体验这个操作系统。一个开发者若不清楚要为之开发的操作系的关键使用模式,就那是对产品的不负责任。

语言应用结构

Java

Objective-C 和 Java 之间有很多不同,虽然若能将 Objective-C 的方式带入 Java 可能会很有诱惑力,但这样做很可能导致代码库与驱动它的主要框架产生冲突。总之,有一些需要提防地陷阱:

  • 类前缀就留在 Objective-C 里不要带过来了。Java 有实在的命名空间和包管理,所以不再需要类前缀。
  • 实例变量的前缀是 m,不是 _。尽可能多的在代码里使用JavaDoc来写方法和类描述,它能让你和其他人更舒服些。
  • Null 检查!Objective-C能妥善处理向nil发送消息,但Java不行。
  • 向属性说再见。如果你想要 setter 和 getter,你只能实际地创建一个 getVariableName()方法,并显式的调用它。使用 this.object 不会调用你自定义地getter,你必须使用 this.getObjct
  • 同样的,给方法名加上 getset 前缀来更好的识别 getter 和 setter 。Java 方法通常写为动作和查询,例如 getCell(),而不是 cellForRowAtIndexPath:

项目结构

Read on →

提高Xcode编译器效率

编译器做些什么?

本文主要探讨一下编译器主要做些什么,以及如何有效的利用编译器。

简单的说,编译器有两个职责:把 Objective-C 代码转化成低级代码,以及对代码做分析,确保代码中没有任何明显的错误。

现在,Xcode 的默认编译器是 clang。本文中我们提到的编译器都表示 clang。clang 的功能是首先对 Objective-C 代码做分析检查,然后将其转换为低级的类汇编代码:LLVM Intermediate Representation(LLVM 中间表达码)。接着 LLVM 会执行相关指令将 LLVM IR 编译成目标平台上的本地字节码,这个过程的完成方式可以是即时编译 (Just-in-time),或在编译的时候完成。

LLVM 指令的一个好处就是可以在支持 LLVM 的任意平台上生成和运行 LLVM 指令。例如,你写的一个 iOS app, 它可以自动的运行在两个完全不同的架构(Inter 和 ARM)上,LLVM 会根据不同的平台将 IR 码转换为对应的本地字节码。

LLVM 的优点主要得益于它的三层式架构 – 第一层支持多种语言作为输入(例如 C, ObjectiveC, C++ 和 Haskell),第二层是一个共享式的优化器(对 LLVM IR 做优化处理),第三层是许多不同的目标平台(例如 Intel, ARM 和 PowerPC)。在这三层式的架构中,如果你想要添加一门语言到 LLVM 中,那么可以把重要精力集中到第一层上,如果想要增加另外一个目标平台,那么你没必要过多的考虑输入语言。在书 The Architecture of Open Source Applications 中 LLVM 的创建者 (Chris Lattner) 写了一章很棒的内容:关于 LLVM 架构

在编译一个源文件时,编译器的处理过程分为几个阶段。要想查看编译 hello.m 源文件需要几个不同的阶段,我们可以让通过 clang 命令观察:

% clang -ccc-print-phases hello.m

0: input, "hello.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, assembler
3: assembler, {2}, object
4: linker, {3}, image
5: bind-arch, "x86_64", {4}, image

本文我们将重点关注第一阶段和第二阶段。在文章 Mach-O Executables 中,Daniel 会对第三阶段和第四阶段进行阐述。

预处理

每当编源译文件的时候,编译器首先做的是一些预处理工作。比如预处理器会处理源文件中的宏定义,将代码中的宏用其对应定义的具体内容进行替换。

例如,如果在源文件中出现下述代码:

1
#import <Foundation/Foundation.h>;

预处理器对这行代码的处理是用 Foundation.h 文件中的内容去替换这行代码,如果 Foundation.h 中也使用了类似的宏引入,则会按照同样的处理方式用各个宏对应的真正代码进行逐级替代。

这也就是为什么人们主张头文件最好尽量少的去引入其他的类或库,因为引入的东西越多,编译器需要做的处理就越多。例如,在头文件中用:

1
@class MyClass;
Read on →

Xcode编译你所不知道的细节

近些日子我们被宠坏了 – 我们只需要单击 Xcode 中的一个按钮(这个按钮看起来有点像是在播放一些音乐的动作),过几秒钟之后,我们的程序就会运行起来了,直到遇到一些错误,这非常的神奇。

在本文中,我们将从更高级别的角度来解读 Build 过程,并探索一下在 Xcode 界面中暴露出的 project setting 信息与 Build 过程有什么关系。为了更加深入的探索 Build 过程中,每一步实际执行的工作,我都会在本文中引入一些别的文章。

解密 Build 日志

为了了解 Xcode build 过程的内部工作原理,我们首先把突破口瞄准完整的 log 文件上。打开 Log Navigator ,从列表中选择一个 Build ,Xcode 会将 log 文件很完美的展现出来。

Xcode build log navigator

默认情况下,上面的 Xcode 界面中隐藏了大量的信息,我们通过选择任务,然后点击右边的展开按钮,就能看到每个任务的详细信息。另外一种可选的方案就是选中列表中的一个或者多个任务,然后选择组合键 Cmd-C,这将会把所有的纯文本信息拷贝至粘贴板。最后,我们还可以选择 Editor 菜单中的 “Copy transcript for shown results”,以此将所有的 log 信息拷贝到粘贴板中。

本文给出的示例中,log 信息将近有 10,000 行(其实大多数的 log 信息是编译 OpenSSL 时生成的,并不是我们自己所写的代码生成的)。下面我们就开始吧!

注意观察输出的 log 信息,首先会发现 log 信息被分为不同的几大块,它们与我们工程中的targets相互对应着:

Build target Pods-SSZipArchive
...
Build target Makefile-openssl
...
Build target Pods-AFNetworking
...
Build target crypto
...
Build target Pods
...
Build target ssl
...
Build target objcio

本文涉及到的工程有几个依赖项:其中 AFNetworking 和 SSZipArchive 包含在 Pods 中,而 OpenSSL 则以子工程的形式包含在工程中。

针对工程中的每个 target,Xcode 都会执行一系列的操作,将相关的源码,根据所选定的平台,转换为机器可读的二进制文件。下面我们详细的了解一下第一个 target:SSZipArchive。

在针对这个 target 输出的 log 信息中,我们可以看到每个任务被执行的详细情况。例如第一个任务是处理一个预编译头文件(为了增强 log 信息的可读性,我省略了许多细节):

(1) ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(2) cd /.../Dev/objcio/Pods
	setenv LANG en_US.US-ASCII
	setenv PATH "..."
(3) /.../Xcode.app/.../clang 
(4) -x objective-c-header 
(5) -arch armv7 
... configuration and warning flags ...
(6) -DDEBUG=1 -DCOCOAPODS=1 
... include paths and more ...
(7) -c 
(8) /.../Pods-SSZipArchive-prefix.pch 
(9) -o /.../Pods-SSZipArchive-prefix.pch.pch
Read on →