IOS工程化探究

199 阅读4分钟

1.Workspace和project

image.png

workspace包含了projectproject包含了targettarget是最终的产物,targetConfiguration配置文件配置,同时也受Scheme控制编译环境(debug和release等)

1.1 workspace

新建一个空白的workspace:

image.png

新建完成后是一个完全空白的workspace

image.png

先看一下 NewWorkspace.xcworkspace 的内容,右键 -> 显示包内容

image.png

其中有三个文件:contents.xcworkspacedata、xcshareddata、xcuserdata

  1. contents.xcworkspacedata

打开后是一个xml文件,里面记录了这个workspace关联了哪些project

image.png 由于当前没有关联project,所以是空的。

  1. xcshareddata 和 xcuserdata

xcshareddata记录了项目中标记为share的Schemes,外部可以使用;xcuserdata记录的就是项目中没有标记为share的Schemes,外部不可使用。

image.png

1.2 project

我们在上面的workspace中新建一个project,先新建一个空project

image.png

好了之后这个project完全空的,并且跟workspace也没有联系 image.png

关闭project,打开workspace,然后左下角点击 + 号,Add Files to "NewWorkspace" image.png

选中project,添加进来

image.png

这个时候 workspace 和 project 就联系起来了,只是这个时候还没有target。

image.png

现在回过头来看 NewWorkspace.xcworkspace,右键 显示包内容,打开 contents.xcworkspacedata 文件,会发现里面记录了 project 文件的路径。

image.png

2.Target和Scheme

2.1 Target

Target是构建项目的目标,这么理解:编译target,得到最终的product。所以target包含了要构建的代码。 在workspace中,选中project之后,点击+ image.png

创建完成后就有target了,也有了对应的代码

image.png

对于target来说,在 Build Settings 中可以配置编译的一些选项,在Build Phases中配置编译哪些代码、依赖哪些库等。

image.png image.png

2.1 Scheme

对于项目来说,管理Target的编译状态以及编译时的传参数,是通过 Scheme image.png

用例:通过切换Scheme来切换编译的产物是 debug环境 或 release环境 新建Scheme:

image.png

image.png

image.png

image.png

image.png

这样就能实现通过切换Scheme来切换编译的产物是 debug环境 或 release环境。

3.xcode内置Shell环境

Xcode把生成产物需要的参数(Build Settings)例如clang需要的参数,以定义shell环境变量的形式,定义在Xcodel的shel环境中。

什么是环境变量?

PATH罗列出shell搜索用户输入的执行命令所在的目录。

递归检测输入文件是否有变更 USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

3.1 将xcode终端的输出,打印到另一个终端上

查看终端标识符

//终端输入:
tty

得到结果:/dev/ttys001

image.png

在xcode的RunScript中,输入 1>tty1 ,1 代表终端的正确的信息,> 代表输出到/dev/ttys001的终端上。

编译项目:

image.png

4.Xcode多环境配置

在项目中右键新建config文件

image.png

config文件以key-value的格式书写

例如:HEADER_SEARCH_PATHS = "${SRCROOT}/Cat"

可以手动指定应用环境:

HEADER_SEARCH_PATHS[config=Release] = "${SRCROOT}/Cat"

写完之后再 Build Settings 中可以看见其修改的内容

image.png

由于 config 文件应用于什么环境下(debug或release)是由Project决定的,所以,在Project中可以修改某个环境,应用哪个config文件

image.png

build Settings 中,点击 Levels 标签,可以查看目前配置的优先级和正在应用的配置是哪个

Resolved标签代表目前应用的配置,然后 配置的优先级依次是:Target手动配置 > Config文件 > project。

image.png

在Target手动配置时,加上 $(inherited) 可以继承Config的配置过来,一起生效。

5.Pods工程探究

xcode Shell中有一个配置,设置为 YES 之后,意味着在Run Script 执行脚本之前,会判断 【input Files、Output Files等】 中的文件有无发生变化,如果有发生变化,才执行脚本,无发生变化就不执行脚本。这么做是为了加快编译速度。

USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

image.png

cocopods在项目中就是通过这个逻辑来规避:不同开发成员由于pods版本不同而产生冲突。

image.png

//diff是对比文件
diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
// $? 是上一句命令的执行结果,如果=0意味着正确,!=0意味着不正确
if [ $? != 0 ] ; then
    echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
    //退出
    exit 1
//结束if语句
fi
# This output is used by Xcode 'outputs' to avoid re-running this script phase.
//将 "SUCCESS" 写入 Output Files 的第一个文件中
echo "SUCCESS" > "${SCRIPT_OUTPUT_FILE_0}"

如果target A要使用到target B的内容,则target A依赖target B:

  • Implicit Dependencies: 隐式依赖。如果target A和B在同一个 project 或者 workspace 下,则Xcode可以自动检测依赖关系。当构建A之前,自动构建B;
  • Explicit Dependencies: 显式依赖。需要手动添加依赖关系

新建一个command line tool工程,一般是这样的:

int main(int argc, const char * argv[]) {
    @autoreleasepool {

    }
    return 0;
}

但其实它还有一个隐藏的参数,叫 char** env,它的作用是可以访问到shell中的环境变量

int main(int argc, const char * argv[], char** env) {

}

Scheme中,可以分别向 int argcchar** env传递参数

int argc是代表 char * argv[]数组中有几个参数

image.png

5.1 打印 const char * argv[] 中的参数

在shell中:parray 3 argv (这里的数字3代表:打印前x个参数)

image.png

argv[]中第一个参数是这个可执行文件存在的目录路径

在代码中打印全部参数

int main(int argc, const char * argv[], char** env) {

    for (int i = 0; i < argc; ++i) {
        NSLog(@"%@", [NSString stringWithUTF8String:argv[i]]);
    }
    return 0;
}

5.2 打印char** env中的环境变量

在shell中:parray 3 env

image.png

char** env中参数都是环境变量,以 key=value 的格式输出。

在代码中打印全部环境变量

int main(int argc, const char * argv[], char** env) {
    //打印环境变量
    while (*env){
        printf("%s\n", *env++);
    }
    //或者这么打印
    return 0;
}

要拿到某个环境变量的参数,C语言提供了一个函数 getenv

char *env = getenv("CCT_ENV");

5.3 -swift文件

在oc使用swift文件的代码,一般这么干:

定义一个swift文件

@objc
public class CCT:NSObject{
    
}

然后在oc中:

#import "CTLibrary-Swift.h"

@implementation CTLibrary

- (instancetype)init{
    
    self = [super init];
    if(self){
    
        //使用swift类
        CCT *cct = [CCT alloc];
    }
    
    return self;
}

我们会发现,导入名为 CTLibrary-swift 文件,在工程目录中不存在,而是存在于:

image.png

hmap 文件同一个目录的在 DerivedSources 文件夹中。

点开CTLibrary-swift文件:

image.png

发现它将swift类,用oc做了一个类声明,所以在oc文件中才能访问到swift类。

那么,如果一个sdk项目,需要把 xxx-swift 提供给外界时,则需要把 xxx-swift 放到头文件目录里面。

于是,可以在 Run Script 写一段脚本:

# copy后接受文件的路径
SWIFT_H_PATH="${SRCROOT}/Swift Compatibility Header/${PRODUCT_NAME}_Swift.h"

# 拷贝文件 ditto + 原路径 + 目标路径
ditto "$DERIVED_FILES_DIR/${PRODUCT_NAME}-Swift.h" "${SWIFT_H_PATH}"

build一下,文件就能拷贝到目标文件夹下了

image.png

6.Pbxproj文件探究与修改

新建一个空白的工程(没有target),然后右键 .xcodeproj 显示包内容,打开 project.pbxproj查看: image.png

这里面有许多个 section, 每个section包裹着这个项目的信息。

再到工程中,新建一个target,选择 command line tool

image.png

这个时候再看 project.pbxproj文件:

image.png

里面的描述就变了,main.m文件 和target都在里面有描述。

具体来说,.pbxproj 文件包含以下几种key:

  • archiveVersion : 当前文件的生成版本
  • objectVersion : 当前文件内objects的描述版本
  • classes : 占位,没有实际意义
  • objects : 字典,以每个object的UUID作为key,object的属性作为value
  • rootObject : 当前文件的根object的UUID(isa=PBXProject)

已知的object类型:

KNOWN_ISAS = {
      'AbstractObject' => %w(
        PBXBuildFile
        AbstractBuildPhase
        PBXBuildRule
        XCBuildConfiguration
        XCConfigurationList
        PBXContainerItemProxy  //用来代指当前project包含的其他project
        PBXFileReference
        PBXGroup
        PBXProject
        PBXTargetDependency
        PBXReferenceProxy     //当前project引用的,相同空间的其他project的文件
        AbstractTarget
      ),

      'AbstractBuildPhase' => %w(
        PBXCopyFilesBuildPhase
        PBXResourcesBuildPhase
        PBXSourcesBuildPhase
        PBXFrameworksBuildPhase
        PBXHeadersBuildPhase
        PBXShellScriptBuildPhase
      ),

      'AbstractTarget' => %w(
        PBXNativeTarget
        PBXAggregateTarget
        PBXLegacyTarget
      ),

      'PBXGroup' => %w(
        XCVersionGroup
        PBXVariantGroup
      ),
    }.freeze

7.index索引