Protocol Buffers(Objective-C)踩坑指南
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Protocol Buffers(Objective-C)踩坑指南相关的知识,希望对你有一定的参考价值。
参考技术A这篇文章是讲如何把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 格式的数据~
以上是关于Protocol Buffers(Objective-C)踩坑指南的主要内容,如果未能解决你的问题,请参考以下文章