当我们新建一个 Cocoa 项目时,Xcode 会提供一系列的模板,类似前端的脚手架工具,只需要简单的几个选项,就可以配置好一个项目所需的基本环境。

这些基本环境配置一般包括:

  • 编译选项、证书链选项

  • 项目 Target、单元测试 Target

  • 基于 git 的版本控制管理

  • 默认的源文件

当然我们也可以新建一个空白的 Project,然后手动去组装这些东西。

由于苹果的封闭性,对 Cocoa 项目的管理基本上都在 Xcode 中进行,这个 All-in-One 的强大工具提供了从文档、编码、调试、测试,再到签名、打包、上线的全流程支持。

随着开发的深入,我们的项目变得越来越复杂,各种链接库、子工程相互引用,不同 Target、Scheme 配置混杂,还会遇到多人协作开发时诡异的冲突。即使是老鸟有时候也会一筹莫展,这时候就需要对 Xcode 工程结构以及管理机制有更加清晰的认识。

Scheme、Target、Project 和 Workspace

Schema、Target、Project 和 Workspace 是组成一个 Xcode 工程最核心的单元,也是我们首先需要理解的部分。

Target

Target 是我们工程中的最小可编译单元,每一个 target 对应一个编译输出,这个输出可以是一个链接库,一个可执行文件或者一个资源包。它定义了这个输出怎样被 build 的所有细节,包括:

  • 编译选项,比如使用的编译器,目标平台,flag,头文件搜索路径等等。

  • 哪些源码或者资源文件会被编译打包,哪些静态库、动态库会被链接。

  • build 时的前置依赖、执行的脚本文件。

  • build 生成目标的签名、Capabilities 等属性。

我们平时在 Build Settings,Build Phases 中配置的各种选项,大部分都是对应到指定的 target 的。

每次我们在 Xcode 中 run/test/profile/analyze/archive 时,都必须指定一个 target。

工程中的 targets 有时候会共享很多代码、资源,这些相似的 targets 可能对应同一个应用的不同版本,比如 iPad 版和 iPhone 版,或者针对不同市场的版本。

Project

Project 很好理解,就是一个 Xcode 工程,它管理这个工程下的 targets 集合以及它们的源码,引用的资源,framework 等等

Project 是管理资源的容器,本身是无法被编译的,所以每个 project 至少应该有一个可编译的 target,否则就是一个空壳。一个 target 编译时引用的资源是它所在 project 所有管理资源的子集。

我们也可以对 project 进行配置,包括基本信息和编译选项(Build Settings)等,这些配置会应用到它管理的所有 targets 中,但是如果 target 有自己的配置,则会覆盖 project 中对应的配置。

在很多情况下,我们的工程中只有一个 project。可以在 finder 中双击后缀名为.xcodeproj 的文件,就可以直接打开单个 project 了。

如果我们需要从源码编译一个依赖库,可以把这些源码所在的工程作为主工程的subProject 添加到目录结构中去:

然后将这个子工程的某个 target 作为主工程 target 的依赖,从而在 build 主工程 target 的时候,顺便也会编译子工程对应的 target。

这样做的好处是你可以在一个窗口中同时修改主工程和子工程的源码,并且一起进行编译。

Workspace

上面所说的添加子工程的方法,已经很好的解决了不同项目中 target 依赖的问题了,那么什么时候需要用到 Workspace 呢?

当一个 target 被多个不同的项目依赖,或者 project 之间互相引用,那么我们就需要把这些 projects 放到相同的层级上来。管理相同层级 projects 的容器就是 Workspace

和 projects,target 不同,workspace 是纯粹的容器,不参与任何编译链接过程,它主要管理:

  • Xcode 中的 projects,记录它们在 Finder 中的引用位置。

  • 一些用户界面的自定义信息(窗口的位置,顺序,偏好等等)。

注意到,当你把不同的 projects 放到一个 workspace 中管理后,你仍然可以用 Xcode 单独打开其中的某一个 project,但是如果它涉及到对其它 project target 的依赖,这时候你无法在这个单独的窗口中编译成功。

在 iOS 开发中,我们常常使用 Cocoapods 来管理三方库,它会把这些三方库的源码组装成一个 project,和主工程一起放入到 workspace 中,自动配置好主工程与 pods 库之间的依赖。所以如果引入了 Cocoapods,我们必须打开这个新的 workspace 才能正常 build 原来的项目。关于 Cocoapods,我们在后面的文章中再详细介绍

Scheme

日常开发中我们常常点击 Xcode 左上角的 Run 箭头来运行调试代码,这其实就是执行了 Scheme 定义的一个任务。

针对一个指定的 target,scheme 定义了 build 这个 target 时使用的配置选项,执行的任务,环境参数等等。Scheme 可以理解为一个工作流,或者蓝图,当我们点击 debug,test 按钮时,Xcode 会按照 scheme 中的定义,去执行对应的工作流。

Scheme 中预设了六个主要的工作流: Build, Run, Test, Profile, Analyze, Archive。包括了我们对某个 target 的所有操作,每一个工作流都可以单独配置。

Scheme 中最重要的一个配置是选择 target 的 build configuration,对每一个 project,会有两个默认的 build configuration:debug 和 release。

每个 configuration 对应了 build target 时不同的参数集,比如宏,编译器选项,bundle name 等等。我们可以在 target 的配置页中更改这些选择项,也可以自己创建新的 build configuration,比如为 App 创建免费和付费版本的配置。

除了 build configuration 外,scheme 还可以配置:

  • 运行时的环境变量(Environment Variables)

  • 启动时设置给 UserDefaults 的参数(Arguments Passed on Launch)

  • App 执行时的系统语言、模拟的定位坐标、国家等环境参数

  • runtime,内存管理,日志,帧率检测等调试选项

另外有一些 debug 时十分有用的选项,也可以在 scheme 配置中找到。

一个 scheme 对应一个 target,同一个 target 可以有多个 scheme,通过灵活地配置 scheme,我们可以方便地管理不同环境下 App 的测试,调试,打包流程。

下表列举了我们在 Scheme 中的常见配置选项:

选项 说明
Pre(Post)-actions 指定在进行工作流之前(之后)执行的脚本,发送邮件通知等任务。
Launch 编译完成后是否立即运行
Arguments Passed On Launch 指定一些运行时的参数,比如本地化语言的选项,Core Data 调试选项等
Environment Variables 指定环境变量,比如开启僵尸内存,Malloc选项,I/O buffer大小等。
Application Language/Region App 运行使用的语言和国家
XPC Services 打开调试 XPC (应用间通信)
Queue Debugging 打开线程调试,会自动记录运行时的线程信息。
Runtime Sanitization 是否打开运行时的一些调试选项,包括内存检测、多线程检测等等,在 debug 一些棘手的异常时十分有用
Memory Management 开启一些内存管理相关的服务,包括内存涂抹,边缘保护,动态内存分配保护,僵尸对象等等
Logging 配置调试过程中终端输出的日志
Runtime Sanitization 是否打开运行时的一些调试选项,包括内存检测、多线程检测等等,在 debug 棘手的异常时十分有用

本文参考:

    • Xcode Concepts (https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Projects.html#//apple_ref/doc/uid/TP40009328-CH5-SW1)

    • Xcode and git: bridging the gap(https://robots.thoughtbot.com/xcode-and-git-bridging-the-gap)

    • Xcode Project vs. Xcode Workspace - Differences (https://stackoverflow.com/questions/21631313/xcode-project-vs-xcode-workspace-differences)

    • limit run script build phase to release configuration (https://stackoverflow.com/questions/3614017/how-can-i-limit-a-run-script-build-phase-to-my-release-configuration)

    • Xcode - xcworkspace and xcodeproj (http://neurocline.github.io/dev/2016/04/16/xcode-xcworkspace-and-xcodeproj.html)

    • Launch Arguments & Environment Variables (https://nshipster.com/launch-arguments-and-environment-variables/)

iOS 开发:深入理解 Xcode 工程结构(一)转的更多相关文章

  1. iOS开发SDWebImageOptions理解

    iOS开发SDWebImageOptions理解 原文 http://www.cnblogs.com/WJJ-Dream/p/5816750.html typedef NS_OPTIONS(NSUIn ...

  2. iOS开发总结-Xcode常见错误

    iOS开发总结-Xcode常见错误 "file/file.h"filenotfound 如果遇到这种类型的问题报错,可以分为三部来解决,由简到复杂一步一步来,直到解决问题位置. 1 ...

  3. 0112.1——iOS开发之理解iOS中的MVC设计模式

    模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程 ...

  4. iOS开发之理解iOS中的MVC设计模式

    模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程 ...

  5. iOS开发_统计xcode代码行数

    如果要统计ios开发代码,包括头文件的,终端命令进入项目目录下,命令如下 find . -name "*.m" -or -name "*.h" -or -nam ...

  6. iOS开发:在Xcode中用Pods管理第三方库

    之前写了一篇 iOS开发:在Swift中调用oc库 ,今天记录一下如何用Pods的方式来管理第三方库,包括Swift/Object-C的库. 在这之前请先查阅Guides.CocoaPods如何使用的 ...

  7. iOS开发 - RunLoop理解

    RunLoop概念 运行循环,一个 run loop 就是一个事件处理的循环,用来不停的调度工作以及处理事件 作用 保持程序的持续运行 监听处理App中的各种事件(触摸事件,定时器事件,selecto ...

  8. iOS开发——UI基础-Xcode资源拷贝

    #1.拷贝资源的时候选择的copy的含义: 是否要将资源拷贝一份到项目中, 如果不勾选就代表着不拷贝, 那么原来的资源不见了, 项目中的也不能用了 注意: 1.虽然项目中的图片和外部的图片是同一张图片 ...

  9. iOS开发:为xcode项目添加git仓储

    现在apple官网下载Command Line Tools 对应mac版本和xcode版本,记录地址:https://developer.apple.com/downloads/ 找到mac的终端,c ...

随机推荐

  1. MS SQL Server数据库查询优化技巧

    [摘 要]本文主要是对MS SQL Server数据库查询优化技巧进行了说明和分析,对索引使用.查询条件以及数据表的设计等进行了阐述.中国论文网 http://www.xzbu.com/2/view- ...

  2. Windows 安装mkvirtualenv虚拟python环境

    pip install virtualenvwrapper-win mkvirtualenv --python=python.exe 新python虚拟环境名称 使用方法 所有的命令可使用:virtu ...

  3. python函数 变量 递归

    1 语法 #语法 def 函数名(参数1,参数2,参数3,...): '''注释''' 函数体 return 返回的值 #函数名要能反映其意义 返回值数=0:返回None放回值数=1:返回object ...

  4. 团队项目个人进展——Day03

    一.昨天工作总结 冲刺第三天,昨天忙着整理数据结构相关知识,在团队项目上只是花了少部分时间来对地图的样式布局进行调整 二.遇到的问题 无 三.今日工作规划 继续昨天的规划,研究地图定位代码,并通过编写 ...

  5. 精华阅读第 12 期 | 最新 App Store 审核指南与10大被拒理由?

    很多时候,我们对技术的追求是没有止境的,我们需要不断的学习,进步,再学习,再进步!本文系移动精英开发俱乐部的第12期文章推荐阅读整理,其中涉及到了 Android 数据库框架,架构设计中的循环引用,同 ...

  6. eclipse安装lua

    单击Eclipse->Help->Install New Software… 在Work with中输入网址 Kepler - http://download.eclipse.org/re ...

  7. leetCode题解之Jewels and Stones

    1.题目描述 2.分析 使用HashTable 是解决这种的好方法. 3.代码 int numJewelsInStones(string J, string S) { map<char,int& ...

  8. RedHat 安装YUM软件

    最近在虚拟机里搭建RedHat Enterprise Linux 6.0 X86_64位系统,在此机器上安装了Oracle11g数据库.其中在安装软件的时候,一般都是用的是RPM命令,但是有些软件包有 ...

  9. 《SQL Server 2008从入门到精通》--20180628

    数据库基本概念:区.页.行 区:SQL Server中管理空间的基本单位.一个区大小为64KB,是八个物理上连续的页.SQL Server中每MB有16个区.一旦一个区被存储满,SQL Server将 ...

  10. ext button 属性

    var buttonName = new Ext.Button({               id:"buttonName",               text:" ...