前言

最近对项目进行优化,就顺便写一些日常开发中会用到的中高级开发技巧。这篇文章聊一下下面三个内容:多环境配置,Mach-O与链接器,Symbol

多环境配置

聊到多环境配置,我们先说几个概念

上图就是每个项目都存在的,下面解释下红框内的内容:

  • 1.Project:包含了项目所有的代码,资源文件,所有信息。
  • 2.Target:对指定代码和资源文件的具体构建方式。
  • 3.Scheme:对指定Target的环境配置。

我尝试加入各种iOS开发交流群,群里的气氛大致就是:学什么iOS,iOS完了,OC完了,群里大致三种人:谁有企业开发证书,马甲包了解一下,至今,大部分iOS开发群还都是仅供吹水用,偶尔能碰见几个好心人解决一下问题,作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!

我们创建好项目,项目默认两个环境Debug和Release环境,在开发过程中我们也会有两套或者多套服务器环境每个环境的地址不同,粗糙的写法就是如下图所示:

只能满足两种环境无法适应多环境,下面我们介绍几种多环境配置方法

Targets进行多环境配置

  • 1.我们可以在Targets中创建多个Target

此时我们只是复制了一个Target,没有产生新的代码,就是复制了一套配置,运行也不会报错

  • 2.我们修改一下Bundle Id
  • 3.修改Info.plist文件

这些修改后,就会发现有两个项目可以选择,分别运行后,就会发现有两个App

通过这个我们可以配置不同icon进行区别

  • 4.设置宏

我们可以根据项目设置自己相应的

配置好后,就可以在项目中进行使用了

总结

这种多环境配置有一下缺点:

  • 1.会存在多个info.plist文件
  • 2.环境多了,配置就会比较乱

下面我们再介绍比这个更好的方式

Configurations多环境配置

我们知道在Scheme中我们是可以选择环境
那可以增加新的环境,增加位置如下:

它和上面得Scheme是一一对应的

添加完成后再看看Scheme就会发现多了个刚加入的Test
image

【问题】:我们每次运行项目都要在上图的Scheme进行切换环境,很不方便,那么有什么办法解决这个问题

  • 1.创建Scheme,如下图位置
  • 2.点击左下角+进行创建,创自己想要的环境名称
  • 3.关闭页面后,再来看项目,就会发现有刚创建好的环境了
  • 4.对创建的Scheme和Configurations关联
  • 5.跟服务器环境进行绑定

    • 1.在项目Build Settings里新建User-Defined,起名就叫HOST_URL
    • 2.在HOST_URL不同的环境配置不同URL
      image
    • 3.在Info.plist进行配置,将近暴露出来
      image
    • 4.在项目中应用
      image
    • 5.验证

总结

此时我们就解决了上面你那个问题,但是这种方式还是不够完美,因为项目中要使用cocoapods对第三方管理,此时用上面的方法对Build Settings设置就比较的繁琐,那么有没有一种方法对Build Settings进行集中管理呢?下面我们介绍另一种方式

.xcconfig多环境配置

初始.xcconfig

在使用cocoapods对第三方进行管理,其实就是通过.xcconfig文件进行管理的,创建好cocoapods会自动根据环境帮我们生成好.xcconfig文件

.xcconfig文件内容是已Key-Value形式存在,'='左侧是key值右侧为value值

在项目里会对不同的环境不同的.xcconfig进行绑定

配置自己的.xcconfig文件

  • 1.创建Config文件,在文件中创建.xcconfig文件

注意命名规则:.xcconfig文件所在的文件夹名称+项目名称+.环境名称

  • 2.根据项目环境配置自己创建的.xcconfig文件

使用.xcconfig

我们还想对不同的环境设置不同的请求地址,那么可以再.xcconfig进行设置

  • 1.创建并设置.xcconfig内容



  • 2.设置plist文件
  • 3.项目中使用
  • 4.验证

总结

这种设置方式要比上面的简化了不少,但是.xcconfig文件的功能不只有这些,下面我们扩展一下

.xcconfig扩展

上面我们介绍了简单的配置URL.xcconfig文件是可以配置Build Settings里面的内容,比如我们平时配置动态库,静态库时,要配置到Other Link Flags,通过.xcconfig我们可以直接配置,例如:我们配置AFN

  • 1.在.xcconfig中写如下代码
  • 2.build一下,再去查看Other Link Flags,发现写的已经配置进去了

通过上面的操作我们可以知道,Build Settings的所有配置都是可以通过.xcconfig文件进行管理的。我们来解释下.xcconfig的内容:OTHER_LDFLAGS是Build Settings里面的一个缩写,其它设置缩写是什么呢?

推荐个地址:Xcode Build Settings

左侧是对应的Build Settings的Key名称,右边则是其对应的缩写

比如我们想配置Header Search Paths,那么在网页上搜索:Header Search Paths,它对应的缩写:HEADER_SEARCH_PATHS

我们在.xcconfig中配置(随便写的地址)
项目Build一下

.xcconfig冲突

问题一

我们项目都会通过cocoapods管理第三方,最开始介绍.xcconfig的时候,说了pods会自动帮我们生成.xcconfig文件,那此时项目中就会有4个.xccongig文件

我们发现一个环境只能配置一个.xcconfig文件,那该怎么办才能让自己写的和pods生成的都能生效呢?

  • 先给我们的TestOC添加Podfile,进行更新

发现报警告,警告的意思是设置好了CocoaPods但是项目中你已经设置了其它的.xcconfig文件导致了Pods设置的.xcconfig文件不会生效,我们要解决这个冲突

  • 有一个关键include它可以引入其他的文件,让引入的文件生效

上面的报错显示了Pods生成的.xcconfig路径:"Target Support Files/Pods-TestOC/Pods-TestOC.debug.xcconfig",我们引入到自己的.xcconfig文件中

  • 运行项目,发现报错

报错说是找不到Pods生成的.xcconfig,我们查看Pods生成的.xcconfig文件,发现它的根目录是Pods

所以需要改成如下路径:
再次运行项目,发现就没问题了
  • 在看Pods会不会报警告

发现Debug模式下不会再报警告,但是Release还会报警告,原因是我们release下的.xcconfig没有配置

问题二

我们看到Pods生成的.xcconfig里也会有OTHER_LDFLAGS,我们自己写的.xcconfig也存在OTHER_LDFLAGS,我们再去看下项目的Other Link Flags,发现Pods生成的.xcconfig没有生效,那么怎么才能让我们写的和Pods生成的都生效呢?

  • 使用inherited进行继承
  • 运行项目
  • 验证
我们将AFN给删掉,在VC调用

总结

我们通过.xcconfig对项目进行配置,我们可以通过.xcconfig+Scheme方式进行统一管理,这样既省时,又省事

Mach-O与链接器

Mach-O初识

【Mach-O定义】:Mach-O(Mach Object)macOS、iOS、iPadOS存储程序和库文件格式。对应系统通过应用二进制接口(application binary interface,缩写为ABI)来运行该格式的文件

Mach-O格式用来替代BSD系统a.out格式Mach-O文件格式保存了在编译过程和链接过程产生机器代码和数据,从而为静态链接动态链接的代码提供单一文件格式

查找项目的Mach-O文件

当我们运行项目是,项目会生成一个.app文件
当我们找到这个.app文件后,显示包内容,里面会有一个可执行文件

我们可执行文件的调用过程大致如下:

  • 1.调用fork函数,创建一个process
  • 2.调用execve或其衍生函数,在该进程上加载,执行我们的Mach-O文件 当我们调用时execve(程序加载器),内核实际上在执行以下操作:
    • 1.将文件加载到内存
    • 2.开始分析Mach-O中的mach_header,以确认它是有效的Mach-O文件

分析Mach-O文件

查看可执行文件有两种方式:

  • 1.通过MachOView,也就是我们所说的烂苹果
  • 2.通过命令:objdump --macho --private-headers

下面我们通过命令行的方式查看可执行文件,其实macho = 文件配置 + 二进制代码 查看Mach-O文件,可以解释项目中的不少问题,我们举几个问题:

  • 项目的入口为什么为main函数
因为在Mach-O中已经指定好了项目入口

红框内的就是指定入口,同样我们可以通过更换指定入口,来达到更换入口的目的

我们还可以通过Mach-O可以看到一些加载系统依赖的库

我们发现这个文件展示的内容很多用不到,需要简化一下,上面说了Mach-O是一个可读写文件,所以可以写一个读取Mach-O的文件,来移除这些不必要的东西(怎么写后面文章在说)

下面使用我们写好的读取Mach-O文件再次读取上面的Mach-O文件

发现少了很多东西,通过这个再次证明Mach-O是可读可写的

链接器

链接的本质

在项目中在编译过程中会生成.o文件(也就是目标文件),如下图所示:

目标文件就是将我们写的代码放到相应的位置,比如我们写的全局变量,代码,全局符号,在编译的过程中会根据不同的特性进行分类

一个项目中会有多个.o文件,而连接的本质就是把多个目标文件组合成一个文件

查看符号

一个项目有很多.m和.h文件,在编译过程中会生成很多的.o文件,那么在合并的过程中我们可以更改暴露给外界的信息吗?

回答这个问题前,我们需要先看到符号表,下面先介绍几个概念:

  • 1.Symbol Table:就是用来保存符号。
  • 2.String Table:就是用来保存符号的名称。
  • 3.Indirect Symbol Table:间接符号表。保存使用的外部符号。更准确一点就是使用的外部动态库的符号。是Symbol Table的子集。

创建调试Demo

我们先弄个项目,让需要打印的符号信息直接展示在终端,方便我们后面调试。这首先会牵扯到私钥和重定向

  • 我们现在Build Phases创建Run Script这个会在运行过程打印信息
  • 新建终端

红框为终端标识,输入tty,会打印一个像链接一样的东西,这个就是这个终端的位置

  • 然后我们将上面的信息copy到Run Script的打印上,对打印地址进行重定向

代码的意思就是讲打印信息重定向到终端

  • 编译代码

可以看到终端打印了我们刚才输入的信息

问题一:怎么让Xcode直行我们需要的代码

  • 这就需要用到我们前面讲的.xcconfig文件,还据上面的例子,以Url为例
  • 改动Run Script指令
  • 编译代码

我们在.xcconfig上配置的地址就给打印出来了

通过脚本打印

  • 这边写好了一个脚本(后面有文章来讲如何写脚本),把重要部分贴出来

这个脚本需要三个参数,分别为:CMD,CMD_FLAG,TTY,我们需要在.xcconfig中对这个三个参数进行赋值

  • 在.xcconfig中赋值

CMD_FLAG中的-pa的意思:-p是不排序的意思-a是显示所有符号,包含调试符号

  • 更改Run Script指令,让其执行我们写的脚本

SRCROOT:代表当前代码路径

  • 都配置好后,进行编译

发现打印出来了

  • 优化,.xcconfig内容改写一下

再次运行还是会成功

写到最后

文章讲的比较细,写的也比较累,后面可能不会写这么细了!文章中间会牵扯到脚本,后面会写文章聊一下脚本语法。这部分文章实际开发中会有所使用,文章没写完,下篇文章会接着这部分继续讲下去。希望大家能够多多交流,共同进步