工作流程
- 链接需要的Framework,例如Foundation.framework,AFNetworking.framework,ALiPay.fframework
- 编译xib文件
- 拷贝xib,图片等资源文件到结果目录
- 编译ImageAssets
- 处理info.plist
- 执行CocoaPod脚本
- 拷贝Swift标准库
- 创建.app文件和对其签名
dSYM 文件
我们在每次编译过后,都会生成一个dsym文件。dsym文件中,存储了16进制的函数地址映射。
在App实际执行的二进制文件中,是通过地址来调用方法的。在App crash的时候,第三方工具(Fabric,友盟等)会帮我们抓到崩溃的调用栈,调用栈里会包含crash地址的调用信息。然后,通过dSYM文件,我们就可以由地址映射到具体的函数位置。
XCode中,选择Window -> Organizer可以看到我们生成的archier文件
iOS 如何调试第三方统计到的崩溃报告
(http://blog.csdn.net/hello_hwc/article/details/50036323)
attribute
或多或少,你都会在第三方库或者iOS的头文件中,见到过attribute。
比如
__attribute__ ((warn_unused_result)) //如果没有使用返回值,编译的时候给出警告
attribtue 是一个高级的的编译器指令,它允许开发者指定更更多的编译检查和一些高级的编译期优化。
分为三种:
- 函数属性 (Function Attribute)
- 类型属性 (Variable Attribute )
- 变量属性 (Type Attribute )
语法结构
attribute 语法格式为:attribute ((attribute-list))
放在声明分号“;”前面。
比如,在三方库中最常见的,声明一个属性或者方法在当前版本弃用了
@property (strong,nonatomic)CLASSNAME * property __deprecated;
这样的好处是:给开发者一个过渡的版本,让开发者知道这个属性被弃用了,应当使用最新的API,但是被__deprecated的属性仍然可以正常使用。如果直接弃用,会导致开发者在更新Pod的时候,代码无法运行了。
__attribtue__的使用场景很多,本文只列举iOS开发中常用的几个:
//弃用API,用作API更新 #define __deprecated __attribute__((deprecated)) //带描述信息的弃用 #define __deprecated_msg(_msg) __attribute__((deprecated(_msg))) //遇到__unavailable的变量/方法,编译器直接抛出Error #define __unavailable __attribute__((unavailable)) //告诉编译器,即使这个变量/方法 没被使用,也不要抛出警告 #define __unused __attribute__((unused)) //和__unused相反 #define __used __attribute__((used)) //如果不使用方法的返回值,进行警告 #define __result_use_check __attribute__((__warn_unused_result__)) //OC方法在Swift中不可用 #define __swift_unavailable(_msg) __attribute__((__availability__(swift, unavailable, message=_msg)))
Clang警告处理
你一定还见过如下代码:
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" ///代码 #pragma clang diagnostic pop
这段代码的作用是
- 对当前编译环境进行压栈
- 忽略-Wundeclared-selector(未声明的)Selector警告
- 编译代码
- 对编译环境进行出栈
通过clang diagnostic push/pop,你可以灵活的控制代码块的编译选项。
- iOS 合理利用Clang警告来提高代码质量 (http://blog.csdn.net/Hello_Hwc/article/details/46425503)
预处理
所谓预处理,就是在编译之前的处理。预处理能够让你定义编译器变量,实现条件编译。
比如,这样的代码很常见
#ifdef DEBUG //... #else //... #endif
同样,我们同样也可以定义其他预处理变量,在XCode-选中Target-build settings中,搜索proprecess。然后点击图中蓝色的加号,可以分别为debug和release两种模式设置预处理宏。
比如我们加上:TestServer,表示在这个宏中的代码运行在测试服务器
然后,配合多个Target(右键Target,选择Duplicate),单独一个Target负责测试服务器。这样我们就不用每次切换测试服务器都要修改代码了。
#ifdef TESTMODE //测试服务器相关的代码 #else //生产服务器相关代码 #endif
插入脚本
通常,如果你使用CocoaPod来管理三方库,那么你的Build Phase是这样子的:
其中:[CP]开头的,就是CocoaPod插入的脚本。
- Check Pods Manifest.lock,用来检查cocoapod管理的三方库是否需要更新
- Embed Pods Framework,运行脚本来链接三方库的静态/动态库
- Copy Pods Resources,运行脚本来拷贝三方库的资源文件
而这些配置信息都存储在这个文件(.xcodeprog)里
到这里,CocoaPod的原理也就大致搞清楚了,通过修改xcodeproject,然后配置编译期脚本,来保证三方库能够正确的编译连接。
同样,我们也可以插入自己的脚本,来做一些额外的事情。比如,每次进行archive的时候,我们都必须手动调整target的build版本,如果一不小心,就会忘记。这个过程,我们可以通过插入脚本自动化。
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}") buildNumber=$(($buildNumber + 1)) /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"
这段脚本其实很简单,读取当前pist的build版本号,然后对其加一,重新写入。
使用起来也很简单:
- Xcode – 选中Target – 选中build phase
- 选择添加Run Script Phase
然后把这段脚本拷贝进去,并且勾选Run Script Only When installing,保证只有我们在安装到设备上的时候,才会执行这段脚本。重命名脚本的名字为Auto Increase build number
然后,拖动这个脚本的到Link Binary With Libraries下面