热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

project_聊聊Xcode项目文件中的project.pbxproj

本文由编程笔记#小编为大家整理,主要介绍了聊聊 Xcode 项目文件中的 project.pbxproj相关的知识,希望对你有一定的参考价值。 来源:杨萧玉(@杨萧玉HIT)  &nb
本文由编程笔记#小编为大家整理,主要介绍了聊聊 Xcode 项目文件中的 project.pbxproj相关的知识,希望对你有一定的参考价值。




来源:杨萧玉(@杨萧玉HIT)   




链接:http://t.cn/Rcg8jUc



project.pbxproj 文件被包含于 Xcode 工程文件 *.xcodeproj 之中,存储着 Xcode 工程的各项配置参数。它本质上是一种旧风格的 Property List 文件,历史可追溯到 NeXT 的 OpenStep。其可读性不如 xml 和 json,苹果却一直沿用至今,作为一家以创新闻名的公司可能这里剩下的就是情怀吧。

本文谈了下 project.pbxproj 的知识,并总结了一些操作工程文件的优秀轮子,并在最后给出了自己的解决方案 pbxprojHelper (https://github.com/yulingtianxia/pbxprojHelper)。


Property List 的历史


想了解 project.pbxproj 文件格式,就需要先了解 Property List。


Property List 有很多种表现方式,最古老的格式就是之前提到的 NeXTSTEP 所使用的格式。那时还算是可读性很强的,仍需要手动编辑。与 json 最明显的差别是:数组用小括号括起来并用逗号隔开元素;字典用大括号括起来并用分号隔开键值对,键值之间用等号连接;二进制数据用尖括号 括起来:


数组:




( "1", "2", "3" )



字典:




{


    "key" = "value";


    ...


}



这也是 project.pbxproj 文件中所使用的格式。


后来出现的 GNUstep 沿用了 NeXTSTEP 格式,并添加了对 NSValue 和 NSDate 对象的支持。到了苹果的 Mac OS X 10.0 推出了新的 XML 格式,旧的 NeXTSTEP 被废弃,只支持读不支持写。这也是为什么使用 plutil 命令或者 Cocoa 的 NSPropertyListSerialization 写入 OpenStep 格式时会报错:Property list format kCFProperty ListOpenStepFormat not supported for writing


因为 XML 语法啰嗦很占空间,苹果在 Mac OS X 10.2 又推出了一种新格式,将 Property List 存储于二进制文件中。虽然在 Mac OS X 10.7 JSON 格式出现了,但是跟 Property List 不兼容。


于是乎 Property List 在苹果家族的历史上存在三种格式:OpenStep,XML 和 Binary。除了 OpenStep 被废弃不支持写入以外,其余格式都提供 API 支持读写。


操作 Property List 的途径


Unix 的 plutil 工具提供了处理 Property list 文件的能力。 比如将 Property list 文件转成 XML 格式:




plutil -convert xml1 -s -r -o project.pbxproj.xml project.pbxproj



-convert 选项可以传入的参数有: xml1, binary1 和 json。


当然 Cocoa 的 NSPropertyListSerialization 也提供了类似的功能,更面向对象。其实 plutil 和 NSPropertyListSerialization 底层都是调用 CoreFoundation 的CFPropertyList 相关的 API,所以功能类似。


使用 NSPropertyListSerialization 读入 project.pbxproj 文件时,字典中键值对的顺序会跟文件中原始的顺序不一致。这是因为字典为了实现快速查找会将 key 按序存储(比如字典序或用红黑树排序)。用 plutil 命令将 project.pbxproj 文件转成 xml 或 json 也会如此。


此外,plutil 命令也支持对某个 keypath 的增、删、改操作。NSPropertyListSerialization 就更不用说了,在程序中随意搞。


之前提到过不支持 OpenStep 写入的问题,所以即便我们能在内存中操作 project.pbxproj 文件,依然不能直接保存。如果自己动手写一个 OpenStep 格式生成程序,依然无法准确还原字典中键值对的顺序。更何况 project.pbxproj 文件中还插入了大量增强 human-readable 的注释,这些注释的生成是有特殊逻辑的,这个在后面会讲。


简要解析 project.pbxproj 文件


既然表面上无法将修改过的工程文件数据还原为 OpenStep 格式,Xcode 又是如何『开挂』做到的呢?这就得从 project.pbxproj 文件内容说起了。


内容规则



可以把整个文件的内容想象成一个字典,字典中的 Key 按照字典序来排列。字典的第一层级总共有 5 个键值对,Key 分别为:archiveVersion,classes,objectVersion,objects 和 rootObject。其中重要的 Key 是 objects 和 rootObject。


所有的配置对象都放在 objects 对应的 Value 中,包括跟对象(rootObject)。 objects 对应的 Value 也是一个字典,Key 都为 UUID,Value 依然是个字典。可以将 rootObject 的值(是一个 UUID)作为 Key 在 objects 对应的字典中找到根对象。这个根对象的 isa 属性为 PBXProject(isa = PBXProject)。读懂 project.pbxproj 的最好方式就是顺着 rootObject 的各个属性对应的 UUID 在 objects 中找到对应的对象,然后一层层看下去。这样整个文件的配置信息存放方式就慢慢摸清了。


objects 中的键值对被分成了若干个 section,虽然 section 的顺序是 Xcode 私有 API 钦定的,但每个 section 内部的键值对会根据 Key 的字典序排列。


每个对象内部的属性(也是键值对)会把 isa 排在最前面,其余的按照字典序排列。


数组内部的顺序完全按照元素内容的字典序排列。


下面是 objects 中 PBXNativeTarget section 的一个对象,感受一下格式:




/* Begin PBXNativeTarget section */


A450185D1D9D68D60002869D /* projectTest */ = {


isa = PBXNativeTarget;


buildConfigurationList = A45018751D9D68D60002869D /* Build configuration list for PBXNativeTarget "projectTest" */;


buildPhases = (


A450185A1D9D68D60002869D /* Sources */,


A450185B1D9D68D60002869D /* Frameworks */,


A450185C1D9D68D60002869D /* Resources */,


);


buildRules = (


);


dependencies = (


);


name = projectTest;


productName = projectTest;


productReference = A450185E1D9D68D60002869D /* projectTest.app */;


productType = "com.apple.product-type.application";


};


/* End PBXNativeTarget section */



可以根据 A45018751D9D68D60002869D 找到对应的 buildConfigurationList 对象的内容,所以说 project.pbxproj 使用 UUID 作为交叉引用的索引。通过这种关系,可以递归构建一张有向图,每个对象都是一个节点。


内容类型


在 Xcode 中能看见所有的公共配置信息都存在于 project.pbxproj 中。主要包含跟文件相关的 BuildFile,Group 和 FileReference;跟编译相关的 BuildPhase 和 Build Configuration(List);以及一些列 Target 和 TargetDependency。


objects 的键值对根据内容类型被分成了若干个 section,采用注释的方式分节也使得可读性更强。section 的数量跟工程有关,尤其是每个工程的 BuildPhase 和 Target 差别都很大。下面列出了一个section 列表(非完整):




PBXBuildFile


PBXBuildPhase


PBXAppleScriptBuildPhase


PBXCopyFilesBuildPhase


PBXFrameworksBuildPhase


PBXHeadersBuildPhase


PBXResourcesBuildPhase


PBXShellScriptBuildPhase


PBXSourcesBuildPhase


PBXContainerItemProxy


PBXFileElement


PBXFileReference


PBXGroup


PBXVariantGroup


PBXTarget


PBXAggregateTarget


PBXLegacyTarget


PBXNativeTarget


PBXProject


PBXTargetDependency


XCBuildConfiguration


XCConfigurationList



每个 section 中的对象类型都是相同的,对象的类型是靠 isa 的值区分的。对象内部的属性类型以及含义可以参照这篇文章提供的对照表:Xcode Project File Format(http://www.monobjc.net/xcode-project-file-format.html


操作 project.pbxproj 文件


我收集了一些可以操作 project.pbxproj 文件的优秀轮子,原理大都是用 plutil 转成 json 或 xml 后进行处理,不仅功能非常局限,且都无法完美还原为 OpenStep 格式的内容:




  • Xcodeproj CocoaPods 写的 Ruby 解析库,用于修改引入 CocoaPods 的工程文件并保存为 XML 格式。CocoaPods 本身是很强大的,还可以用来操作 Xcode workspaces (.xcworkspace), configuration files (.xcconfig) 和 Xcode Scheme files (.xcscheme).


  • mod-pbxproj 强大的 Python 解析库,支持一定的修改操作,可输出 OpenStep 格式,但是顺序和注释内容无法完美还原,有些鸡肋。


  • xUnique 用 Python 写的统一多设备生成的 UUID 的工具,主要用途是统一工程在多设备上生成的 UUID,避免工程文件冲突。


  • pbxplorer Ruby 写的解析库。


  • node-xcode Cordova 基于它管理 Xcode 工程



不过 Xcode 可以打开 XML 格式的 project.pbxproj,一旦在 Xcode 界面上修改工程配置就会重新将 project.pbxproj 转成 OpenStep 风格。解铃还须系铃人,经过多番对比之后发现最终还是 Xcode 自己才能将 XML 完美还原成原来的 OpenStep 格式,且 diff 对比毫无差错。原因很简单,Xcode 使用的私有 API 的导出结果是个黑盒,外界无论怎么猜都会有瑕疵。所以还是导出为 XML 后手动在 Xcode 界面中触发下吧。既然这样的话,如果能够简单高效地生成出 XML 文件作为工程文件就好了。基于此想法我开发了一款叫做 pbxprojHelper 的 Mac App:




操作简单粗暴:




  • 选择一个工程文件然后内容会自动解析在下面的 Outline 列表中,Filter 输入框便于过滤查看内容。


  • 单击 Outline 列表中的文字即可复制内容到剪贴板,双击复制整个keypath!


  • 对 project.pbxproj 文件的增删改操作都配置在 json 文件中,每次想对工程进行修改只需选择对应的 json 配置文件然后点击 “Apply” 即可完成写入替换哦!


  • 不小心误操作的话还可以点 “Revert” 回滚到上个版本哦!


  • 什么?懒得写 json 配置文件?下面这个附带的 json 配置生成器可以帮你直接生成一个哦!使用 ⇧⌘0 快捷键即可召唤此神器!选择两个工程文件和 json 保存路径后轻轻一点 “Generate” 就搞定咯:





所以处理工程文件的正确姿势是:




  • 拷贝出一份原始的 project.pbxproj 文件


  • 在 Xcode 界面上修改工程配置,比如修改编译选项,使用自己的证书等


  • 使用 pbxprojHelper 的 JSON Configuration Generator 来对比修改后的工程文件和原始的工程文件,自动生成 JSON 配置文件


  • 以后想要在工程文件上施加自己的修改时,只需要应用之前生成好的 JSON 配置文件即可



pbxprojHelper 的优势在于可以自由地增删改查任意属性,原生 UI 降低了使用门槛。功能强大的同时人性化的设计使得更快捷浏览工程文件中的内容。无需写任何代码即可一键配置自己想要的工程文件


此外还提供了命令行工具 pbxproj, 它具有 pbxprojHelper.app 具有的大部分功能:




Usage: pbxproj [command_option] file


Command options are (-convert is the default):


-compare modified_file -o path          compare modified property list file with property list file and generate a json result at the given path


-apply json_file                        apply a json file on property list file


-revert                                 revert property list file to latest backup


-convert                                rewrite property list files in xml format



可以使用 pbxproj 搭配 DevToolsCore 私有 framework 来完成修改工程文件并转化成 OpenStep 格式的一条龙自动化程序。


你可以在 GitHub 上下载最新的 Release 版。或者在 App Store 中下载:https://itunes.apple.com/cn/app/pbxprojhelper/id1160801848?mt=12


本项目完全手撸,没依赖上面提到的任何轮子

推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文讲述了孙悟空写给白骨精的信件引发的思考和反省。孙悟空在信中对自己的行为进行了反思,认识到自己胡闹的行为并没有给他带来实际的收获。他也揭示了西天取经的真相,认为这是玉皇、菩萨设下的一场陷阱。他还提到了师傅的虚伪和对自己的实心话,以及自己作为师傅准备提拔的对象而被派下来锻炼的经历。他认为路上的九九八十一难也都是菩萨算计好的,唐僧并没有真正的危险。最后,他提到了观音菩萨在关键时刻的指导。这封信件引发了孙悟空对自己行为的思考和反省,对西天取经的目的和自己的角色有了更深入的认识。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • Node.js学习笔记(一)package.json及cnpm
    本文介绍了Node.js中包的概念,以及如何使用包来统一管理具有相互依赖关系的模块。同时还介绍了NPM(Node Package Manager)的基本介绍和使用方法,以及如何通过NPM下载第三方模块。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
author-avatar
dyh81216462
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有