通过Xcodeproj深入探究Xcode工程文件 二

前言

上文介绍了Xcode的配置文件project.pbxproj里面的内容并且提到了Cocoapods正是利用Xcodeproj这个组件实现修改该文件达到改变Xcode工程结构的效果。本文将着重介绍Xcodeproj这个组件,通过本文你将会了解这个组件的内容、原理和使用该组件的应用场景。

介绍

Xcodeproj作为Cocoapods的组件之一,它能够允许你用Ruby语言创建或者修改Xcode工程,脚本化枯燥的管理任务和构造友好的Xcode库,它同时支持Xcode workspaces (.xcworkspace)configuration files (.xcconfig)Xcode Scheme files (.xcscheme)

它的API文档在这里

安装

Xcodeproj通过RubyGems安装,打开终端键入


$ [sudo] gem install xcodeproj

结束后,输入gem list查看Xcodeproj是否完成安装,正常情况下你会在list中看到xcodeproj (1.2.0, 1.1.0, 0.28.2)这一行。

内容

让我们来大体瞅一眼Xcodeproj的内容(Class List),如图1


图1

看到库里面的各个类,是不是有点小激动?没错,就是上篇文章介绍过的project.pbxproj里面的各个元素,连名字都是一样!单独看下PBXProject中的各个Attributes(图2),再拿上文中project.pbxproj(图3)里的进行对比


图2


图3

你会发现Xcode配置文件中元素每个属性都能在这个库同名类中找到对应的属性。值得注意的是,Xcodeproj中所有的类都继承于AbstractObject,这个类是个基类,里面有isa,uuid,project,其中uuid就是唯一标识符,还有其他一些基本的method。这个唯一标识符的生成过程在uuid_generator.rb这个类中,笔者水平有限,仅能看出uuid的生成算法加入了文件路径的MD5

实战

下面你们可以通过下面这三个实战例子感受下Xcodeproj的强大,代码如下:

	
require 'xcodeproj'
project_path = `.......`    # 工程的全路径 注意这里用单引号''不要用``会出问题的
project = Xcodeproj::Project.open(project_path)

 # 1、显示所有的target
project.targets.each do |target|
  puts target.name
end

# 2、显示第一个target的所有Compile Sources
target = project.targets.first
files = target.source_build_phase.files.to_a.map do |pbx_build_file|
    pbx_build_file.file_ref.real_path.to_s
end.select do |path|
  path.end_with?(".m", ".mm", ".swift")
end.select do |path|
  puts path
end

# 3、创建一个target 并添加文件
app_target = project.new_target(:application, 'demo', :ios, '6.0')
header_ref = project.main_group.new_file('./Class.h')
implm_ref = project.main_group.new_file('./Class.m')
app_target.add_file_references([implm_ref])
project.save()

大家可以写个ruby脚本依次将三个实例执行下,注意观察终端输出和Xcode目录结构的变化。

原理

如果你已经执行了上线的操作,那么一定好奇,这个库是怎么操作project.pbxproj文件的?首先需要知道的是,在这个库操作project.pbxproj之前,需要把Xcode工程的全路径给它,那我们就从Project入手,它对应的是上篇文章中提到的根元素,从open开始,注意我代码中的注释!

	
# File 'lib/xcodeproj/project.rb', line 96
def self.open(path)
  path = Pathname.pwd + path
  unless Pathname.new(path).exist?
    raise "[Xcodeproj] Unable to open `#{path}` because it doesn't exist."
  end
  project = new(path, true) 
  project.send(:initialize_from_file)  # 执行这个方法之前会判断path的正确性
  project
end

# File 'lib/xcodeproj/project.rb', line 96
def initialize_from_file
  pbxproj_path = path + 'project.pbxproj'  # 拿到包内容中的配置文件,这个地方操作的是根元素
  plist = Plist.read_from_path(pbxproj_path.to_s) 
  root_object.remove_referrer(self) if root_object
  @root_object     = new_from_plist(plist['rootObject'], plist['objects'], self)  # new_from_plist方法拿到rootObject,正式开始操作
  @archive_version = plist['archiveVersion']
  @object_version  = plist['objectVersion']
  @classes         = plist['classes']
  @dirty           = false

  ......
end 

# File 'lib/xcodeproj/project.rb', line 252
def new_from_plist(uuid, objects_by_uuid_plist, root_object = false)
  attributes = objects_by_uuid_plist[uuid]
  if attributes
    klass = Object.const_get(attributes['isa'])
    object = klass.new(self, uuid)
    objects_by_uuid[uuid] = object
    object.add_referrer(self) if root_object
    object.configure_with_plist(objects_by_uuid_plist) # 分析plist
    object
  end
end

到了这里,从根元素进入,分析objects属性内的所有元素,configure_with_plist中使用objects的uuid去分析包装相应元素,将其装变为库中的对应类的对象,同时isa也被复制过去。
最终,project.pbxproj中的所有元素对应的信息,都转化为Ruby对象,然后增删改查等操作都变为对象操作,使用起来非常方便。

使用场景

  • 你可以做一个Ruby脚本,放在打包测试流程中去,用来分析项目中不同target中缺少的文件和资源。
  • 将一些繁琐的配置操作写成一个脚本,省时省力

原文链接:http://www.tomorjm.com/year/10/06/通过Xcodeproj深入探究Xcode工程文件%20二/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值