文章首发于个人blog
欢迎指正补充,可联系[email protected]
原文地址:iOS中FlatBuffers的使用
官方文档
GitHub - Google / flatbuffers
FlatBuffer官网
iOS 相关Demo
FlatBuffers-ObjC
NSStream
FlatBuffers_useage
FlatBuffer
FlatBuffersSwift
视频
JSON之后的下一代数据交换格式FlaterBuffer
blog
推荐 -- 深入浅出 FlatBuffers 之 Encode
推荐 -- 深入浅出 FlatBuffers 之 Schema
Android 开发者应该使用 FlatBuffers 替代 JSON ?
-- 对应的Demo - FlatBuffer
FlatBuffers 体验
FlatBuffers入门简介
flatBuffer安装及使用教程
开源、高效、跨平台:深剖Google FlatBuffers工作原理
NSStream封装和FlatBuffer数据解析
-- 对应的Demo - NSStream
高效数据序列化的工具 FlatBuffers 的初体验
目录
-
一、
JSON
与FlatBuffers
比较与使用场景 -
二、
FlatBuffers
简单使用(iOS无法使用该方法,直接看下一节)-
2.1、
CMake
安装 -
2.2、编译生成
flatc
工具 -
2.3、编写
FlatBuffers
的scheme
文件 -
2.4、使用
flatc
工具编译scheme
文件,生成对应语言的数据对象头文件/类 -
2.5、使用各自语言的
FlatBuffers
库 序列化对象/反序列化数据对象
-
-
三、
iOS
从零使用FlatBuffers
-
四、
FlatBuffers
原理-
4.1、官网概述
-
4.2、拿我们iOS的Demo做个分析
-
一、JSON
与 FlatBuffers
比较与使用场景
了解更多:FlatBuffers 体验
1.1、JSON 简介
优点:易读性,跨平台
缺点:性能差,内存占用多,需要解析、打包
JSON是不依赖于开发语言的数据格式,但是在解析数据并将其转换到如Java对象时,会消耗我们的时间和存储资源。
1.2、FlatBuffers 简介
优点:
1、直接读取序列化数据,而不需要解析(Parsing)或者解包(Unpacking):FlatBuffer 把数据层级结构保存在一个扁平化的二进制缓存(一维数组)中,同时能够保持直接获取里面的结构化数据,而不需要解析,并且还能保证数据结构变化的前后向兼容。
2、高效的内存使用和速度:FlatBuffer 使用过程中,不需要额外的内存,几乎接近原始数据在内存中的大小。
3、灵活:数据能够前后向兼容,并且能够灵活控制你的数据结构。
4、很少的代码侵入性:使用少量的自动生成的代码即可实现。
5、强数据类性,易于使用,跨平台,几乎语言无关。
缺点:
1、FlatBuffers 需要生成代码,对代码有侵入性;
2、数据序列化没有可读性,不方便 Debug;
3、构建 FlatBuffers 对象比较麻烦,不直观,特别是如果对象比较复杂情况下需要写大段的代码;
4、数据的所有内容需要使用 Schema 严格定义,灵活性不如 JSON。
FlatBuffers是Google专门为游戏开发而创建的跨平台序列化库,FlatBuffers
是一个开源的跨平台数据序列化库,可以应用到几乎任何语言(C++, C#, Go, Java, JavaScript, PHP, Python)
使用建议
项目中有大量数据传输和解析,使用 JSON 成为了性能瓶颈;
稳定的数据结构定义。
二、FlatBuffers
简单使用(iOS无法使用该方法,直接看下一节)
2.1、Cmake安装
2.1.1、查看是否安装cmake
cmake --version
2.1.2、Mac安装cmake
方法一:推荐Homebrew自动安装
Homebrew简介
- 是否安装
brew
brew --version
- 安装
brew
只需要一条命令即可:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- brew常用命令
搜索软件:brew search 软件名,如brew search wget
安装软件:brew install 软件名,如brew install wget
卸载软件:brew remove 软件名,如brew remove wget
cmake具体安装
- 第一步:brew search cmake
- 第二步:brew install cmake
- 第三步:cmake --version
方法二:手动下载安装
前往 cmake 官网,下载Mac操作系统对应的安装包。
2.2、编译生成 flatc
工具
官网building
也可以参考blog - FlatBuffers入门简介
前往 flatbuffers 下载源码
前往本地
flatbuffers
根目录下,输入如下命令:
cmake -G "Unix Makefiles"
- 稍等一会cmake就完成了MakeFile的生成,接下来运行:
make
- 开始编译,稍等一会编译成功后会在根目录下生成flatc工具。
接下来我们使用
make install
该命令,安装flatc,该命令将flatc工具拷贝到/usr/local/bin/目录下(环境配置不同可能有所不同),重新启动终端输入
flatc --version
- 最后可以在源码中发现,多出一个
flatc
的文件
2.3、编写FlatBuffers的scheme文件
官网有样例 Tutorial,也可以前往 官网-Writing a schema 查看其规则。
// example IDL file
namespace MyGame;
attribute "priority";
enum Color : byte { Red = 1, Green, Blue }
union Any { Monster, Weapon, Pickup }
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3;
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated, priority: 1);
inventory:[ubyte];
color:Color = Blue;
test:Any;
}
root_type Monster;
2.4、使用 flatc 工具编译 scheme 文件,生成对应语言的数据对象头文件/类
了解更多-Using the schema compiler
例子:
./flatc --java Person.fbs
2.5、使用各自语言的 FlatBuffers 库 序列化对象/反序列化数据对象
Android Sample Application
三、iOS从零使用flatbuffers
系统环境:MacOS
编程语言:OC
说明:
如果我们通过 flatbuffers 官网下载的代码,并通过上面一系列的流程后,当走到使用 flatc
工具编译生成 特定的OC的 .h 和 .m 文件
时候发现,生成失败。所以,我不得不重新找其他的方法。
./flatc -oc AAA.fbs
在github找到 FlatBuffers-ObjC 库
ReadME如下:
1. git clone [https://github.com/jidibingren/FlatBuffers-ObjC.git](https://github.com/jidibingren/FlatBuffers-ObjC.git)
2. open rootdir/FlatBuffer/build_ide/Xcode/FlatBuffers.xcodeproj and build flatc target
3. ./flatc -oc test.fbs
4. import the files into your project generated for step 3
5. add pod 'FlatBuffers-ObjC' to your Podfile
3.1、按照库的指引进行生成 flatc
工具
open rootdir/FlatBuffer/build_ide/Xcode/FlatBuffers.xcodeproj and build flatc target
会自动生成 flatc
工具
3.2、编译 scheme 文件
./flatc -oc test.fbs
3.3、实际项目使用
对象编码、解码
Person *person = [[Person alloc]init];
person.name = @"张三";
person.sex = @"男";
// 编码:对象->data
NSData * data = [person getData];
// 解码:
Person *person = (Person *)[Person getRootAs:data];
NSLog(@"name == %@ sex == %@", person.name, person.sex);
四、FlatBuffers
原理
了解更多-FlatBuffers 体验
4.1、官网概述
FlatBuffers 就像它的名字所表示的一样,就是把结构化的对象,用一个扁平化(Flat)的缓冲区保存,简单的来说就是把内存对象数据,保存在一个一维的数组中。借用 Facebook 文章 的一张图如下:
可见,FlatBuffers 保存在一个 byte 数组中,有一个“支点”指针(pivot point)以此为界,存储的内容分为两个部分:元数据和数据内容。其中元数据部分就是数据在前面,其长度等于对象中的字段数量,每个 byte 保存对应字段内容在数组中的索引(从支点位置开始计算)。
如图,上面的 Person 对象第一个字段是 name,其值的索引位置是 1,所以从索引位置 1 开始的字符串,就是 name 字段的值 "John"。第二个字段是 friendshipStatus,其索引值是 6,找到值为 2, 表示 NotFriend。第三个字段是 spouse,也一个 Person 对象,索引值是 12,指向的是此对象的支点位置。第四个字段是一个数组,图中表示的数组为空,所以索引值是 0。
通过上面的解析,可以看出,FlatBuffers 通过自己分配和管理对象的存储,使对象在内存中就是线性结构化的,直接可以把内存内容保存或者发送出去,加载“解析”数据只需要把 byte 数组加载到内存中即可,不需要任何解析,也不产生任何中间变量。
它与具体的机器或者运行环境无关,例如在 Java 中,对象内的内存不依赖 Java 虚拟机的堆内存分配策略实现,所以也是跨平台的。
4.2、拿我们iOS的Demo做个分析
Person *person = [[Person alloc]init];
person.name = @"张三";
person.sex = @"男";
// 编码:对象->data
NSData * data = [person getData];
回头再看看Flatc生成的person.m文件中的内容
- (NSString *) name {
_name = [self fb_getString:4 origin:_name];
return _name;
}
- (void) add_name {
[self fb_addString:_name voffset:4 offset:4];
return ;
}
- (NSString *) sex {
_sex = [self fb_getString:6 origin:_sex];
return _sex;
}
- (void) add_sex {
[self fb_addString:_sex voffset:6 offset:8];
return ;
}
- (instancetype)init{
if (self = [super init]) {
bb_pos = 14;
origin_size = 12+bb_pos;
bb = [[FBMutableData alloc]initWithLength:origin_size];
[bb setInt32:bb_pos offset:0];
[bb setInt32:8 offset:bb_pos];
[bb setInt16:8 offset:bb_pos-[bb getInt32:bb_pos]];
[bb setInt16:12 offset:bb_pos-[bb getInt32:bb_pos]+2];
}
return self;
}
发现代码内部也给我们设置一定的偏移参数,具体的没有深入研究,仅仅看到表面,我们大概也猜到这是在为我们的编码进行准备。
最后我们验证下,在我们的bytes数组中,是否有@"张三"、@"男"等数据的存在,这里我们单独将@"张三"、@"男"转成bytes数组
NSString * nametest = @"张三";
NSString * sextest = @"男";
// NSString --> NSData
NSData *nametestdata = [nametest dataUsingEncoding:NSUTF8StringEncoding];
NSData *sextestdata = [sextest dataUsingEncoding:NSUTF8StringEncoding];
// NSData --> Byte
Byte *nametestbytes = (Byte *)[nametestdata bytes];
Byte *sextestbytes = (Byte *)[sextestdata bytes];
for (int j = 0; j < nametestdata.length; j++) {
NSLog(@"nametestbytes[%d] = %d",j,nametestbytes[j]);
}
for (int j = 0; j < sextestdata.length; j++) {
NSLog(@"sextestbytes[%d] = %d",j,sextestbytes[j]);
}
结果
到了这里我们发现,"张三"、"男"数据都在 flatbuffers 中,并且也在数据内容这边,与官网描述的原理是一致的。
但是flatc生成的OC对象时候是如何分配存储空间的,如何确定"支点"、"元数据"、"数据内容"的暂时还没有深入研究,此处也只是简单的了解下原理。期待各位更深入的研究。