Protocol Buffers(Objective-C)踩坑指南

Protocol Buffers(Objective-C)踩坑指南,第1张

这篇文章是讲如何把protobuf文件的编译工作集成到Xcode中,达到在Xcode中就像添加一般的OC文件一样不进行任何多余的尘冲 *** 作直接编译运行.proto文件判兄态的目的。

牛逼,这么智能吗?是的,就是这么智能!

笔者的公司现在所有端都在统一使用一套protobuf数据结构,免除了多端重复定义同一套数据结构的重复工作,效率很高,非常值得推荐。并且Xcode 10进行了一些小优化来增加了对Protobuf的支持,相信不久以后,Xcode对Protobuf的支持将更加智能!

至于什么是 Protobuf 和 Protobuf 语法教程,不是这篇文章的主题,请自行Google。

环境:Xcode 10+

语言:Objective-C

话不多说,正题开始:

首先,真正的企业级项目,并不只是网上很多教程里面演示的一两个 .proto 文件,而是一批 .proto 文件目录的集合,并且是多端共享的。你会发现按照那些教程里面的讲的去做写个demo或许可以,但是真正要达到企业级别的使用的时候,还远远不够,你会遇到各种各样的坑。别问我是怎么知道的,我都是靠自己一个个踩出来的。

首先,要能编译Protobuf文件,我们得安装官方的编译器。你可以选择下面任意一种你喜欢的安装方式:

安装好后,在terminal中输入 which protoc 检测是否安装成功,如安装成功会返回文件路径: /usr/local/bin/protoc

如有问题,请自行google,不在本教程范围内。

没什么好说的,新建一个Xcode工程。使用Cocoapods引入Protobuf的库:

Pod search Protobuf

选择最稳定的版本即可。

这里有两种创建.proto文件的方式:

至于文件内容,如果你熟悉protobuf语法,那随便写几行即可,如果不熟悉,那么可以copy我的测试内容:

A.proto 文件内容:

B.proto 文件内容:

Xcode 自己并不认识 .proto文件,所以并不会自动编译它们,我们需要把 .proto编译器 自己集成到项目当中,集成的方式如下:

Project -->Build Rules -->点击+号 ,生成一个特定文件类型编译脚本。

比如:

到此处,我们有几个注意事项:

我们试试把 --proto_path 换成相对路径,看会发生什么,也就是把脚本换成

编译运行,咦~报错了。查看日志,我们可以看到这么一条log信息:

翻译过来就是在--proto_path这个参数中你必须指定.proto源文件的精确路径, protoc 太笨了,它无法搞清楚这个相对路径是不是我们要的绝对路径。google的工程师说这太他么难了。所以这里很明确了, --proto_path 的参数值,只能是proto文件根目录的绝对路径。

我们上面说了,${INPUT_FILE_PATH} 是代表编译输入源文件的绝对路径。

文档里面给的demo是:

protoc --proto_path=src --objc_out=build/gen src/foo.proto src/bar/baz.proto

什么意思呢?

它说,最终掘源编译器会把 src/foo.proto 文件编译成: build/gen/Foo.pbobjc.h 和 build/gen/Foo.pbobjc.m 文件。

而会把 src/bar/baz.proto 文件编译成 build/gen/bar/Baz.pbobjc.h 和 build/gen/bar/Baz.pbobjc.m 。

而不是 build/gen/Baz.pbobjc.h 和 build/gen/Baz.pbobjc.m

也就是说protobuf编译器最终生成的文件会自动按照文件源目录结构存放。

特别强调 并不会 自动创建 build/gen 目录,这个目录需要你提前建好。

并且,查看最终编译生成的.m文件,你会发现一些有趣的事情;比如我在A.proto中引入了B.proto文件,你会看到Protobuf最终编译出来的A.pbobjc.m文件导入文件的格式是包含文件路径的,例如:

我们注意到,上面设置的proto文件的编译输出路径是 $DERIVED_FILE_DIR , 这是为何呢?

答案是为了方便Xcode的集成。

对于自定义的编译脚本,都需要设置一个文件的输出路径.

我们点脚本框下面的Output Files下面的 + 号, 指定文件输出路径。

因为OC文件分为.h和.m文件,所以我们指定2个。

点了之后,你会发现,xcode默认给出的是 $(DERIVED_FILE_DIR)/newOutputFile ,

我们将其改为 $(DERIVED_FILE_DIR)/${INPUT_FILE_BASE}.pbobjc.h 和 $(DERIVED_FILE_DIR)/${INPUT_FILE_BASE}.pbobjc.m ,并且在.m文件的 Compiler Flags 中指定为 -fno-objc-arc 代表该.m文件采用mrc编译。

编译运行,大功告成,是不可能的!!!!

你会发现又报错了:

什么意思呢? 其实就是在 DerivedSources 下找不到 A.pbobjc.m 文件。因为我们指定这个编译的输出路径在这个目录下,所以Xcode在进行OC文件的编译时会去这个目录下找,但是它找不到。为什么找不到呢?我们去这个目录下看,这个目录下确实没有 A.pbobjc.m 这个文件,但是确发现有 a/A.pbobjc.m 。原因我们已经说了,protoc最终的编译文件会自动加上目录前缀。

有人可能会说,能不能把输出文件改成 $(DERIVED_FILE_DIR)/*/${INPUT_FILE_BASE}.pbobjc.h 呢?那我们就来试下。

编译运行

what the hell?

原来,Xcode的Output Files特别蠢,它不支持类似这种通配符写法: $(DERIVED_FILE_DIR)/*/${INPUT_FILE_BASE}.pbobjc.h 。

也不支持传入任何的自定义变量。

只能是明确的文件路径和Xcode自带的环境变量,但是实际项目中,可能不只一层路径,有可能是文件夹下嵌套文件夹。

靠,那这怎么办呢?

实在没办法了,就在打算放弃的时候,咨询了我们的脚本大神,我们尝试了以下在脚本末尾再加了两行:

是不是很机智?

什么意思呢?就是说我们cd到该目录,然后找到该文件对应生成的oc文件,将其copy一份儿到根目录。怀着求神拜佛的意志,运行了以下,Perfect,终于不再报错了,到目录中查看,也正是我们想要的,所有文件都被copy出来了。

下一步,就是正常的在项目中import和使用了。

你以为到此就没有坑了吗?到此还有坑。有2点需要注意:

好了,就讲到这里吧,如果觉得文章看得不是很明白,需要一个demo。或者大神有更好的建议,请在评论区留言~

如果文章对你有帮助,请不要吝啬你的点赞哦,你的支持是我分享的动力~

如果大家喜欢,有时间再讲讲怎么改改AFNetworking,能直接请求后端给的 Protobuf 格式的数据~

源文件.h/.m /.cpp ->预编译 ->编译 ->汇编 ->链接(动态库.a/.lib/.framwork)- 可执行文件

源文件:载入.h、.m、.cpp等文件

预处理:替换宏,删除注释,展开头文件,产生.i文件

编译:将.i文件转换为汇编语言,产生.s文件

汇编:将汇编文件转换为机器码文件,产生.o文件

链腔搏接:对.o文件中引用其他库的地方进行引用,生成最后的可执行文件

在App被编译打包成可执行文件格式的Mach-O文件后,交给dyld负责连接,加载程序。

APP启动 ->加载libSystem -> Runtime向注册回调函数 -> 加载新image ->执行map_images Load_imags** ->调用main函数

dyld流程分析 ->dyld_start ->dyldbootstrap::start ->dyld::_main->

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/12312051.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存