iOS中FlatBuffers的使用

文章首发于个人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 的初体验

目录

  • 一、JSONFlatBuffers 比较与使用场景

  • 二、FlatBuffers 简单使用(iOS无法使用该方法,直接看下一节

    • 2.1、CMake 安装
    • 2.2、编译生成 flatc 工具
    • 2.3、编写 FlatBuffersscheme 文件
    • 2.4、使用 flatc 工具编译 scheme 文件,生成对应语言的数据对象头文件/类
    • 2.5、使用各自语言的 FlatBuffers 库 序列化对象/反序列化数据对象
  • 三、iOS 从零使用 FlatBuffers

  • 四、FlatBuffers 原理

    • 4.1、官网概述
    • 4.2、拿我们iOS的Demo做个分析


一、JSONFlatBuffers 比较与使用场景

了解更多:FlatBuffers 体验

1.1、JSON 简介

优点:易读性,跨平台
缺点:性能差,内存占用多,需要解析、打包

JSON是不依赖于开发语言的数据格式,但是在解析数据并将其转换到如Java对象时,会消耗我们的时间和存储资源。

iOS中FlatBuffers的使用_第1张图片
JSON数据传输过程

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 search cmake
  • 第二步:brew install cmake
iOS中FlatBuffers的使用_第2张图片
brew install cmake
  • 第三步:cmake --version
cmake --version
方法二:手动下载安装

前往 cmake 官网,下载Mac操作系统对应的安装包。

iOS中FlatBuffers的使用_第3张图片
下载Mac指定包

2.2、编译生成 flatc 工具

官网building
也可以参考blog - FlatBuffers入门简介

  • 前往 flatbuffers 下载源码

  • 前往本地 flatbuffers 根目录下,输入如下命令:

cmake -G "Unix Makefiles"
iOS中FlatBuffers的使用_第4张图片
过程图
  • 稍等一会cmake就完成了MakeFile的生成,接下来运行:
make
iOS中FlatBuffers的使用_第5张图片
make过程图
  • 开始编译,稍等一会编译成功后会在根目录下生成flatc工具。
    接下来我们使用
make install
iOS中FlatBuffers的使用_第6张图片
install过程图

该命令,安装flatc,该命令将flatc工具拷贝到/usr/local/bin/目录下(环境配置不同可能有所不同),重新启动终端输入

flatc --version
输出flatc版本号
  • 最后可以在源码中发现,多出一个flatc 的文件
iOS中FlatBuffers的使用_第7张图片
多出一个文件

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 
iOS中FlatBuffers的使用_第8张图片
生成失败

在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 工具

iOS中FlatBuffers的使用_第9张图片
flatc工具
3.2、编译 scheme 文件
./flatc -oc test.fbs
iOS中FlatBuffers的使用_第10张图片
OC文件
3.3、实际项目使用
iOS中FlatBuffers的使用_第11张图片
导入需要的一些文件
iOS中FlatBuffers的使用_第12张图片
项目截图
对象编码、解码
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 文章 的一张图如下:

iOS中FlatBuffers的使用_第13张图片
flatbuffers

可见,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];
iOS中FlatBuffers的使用_第14张图片
根据person对象编码生成的Bytes数组

回头再看看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]);
}

结果

iOS中FlatBuffers的使用_第15张图片
@"张三"的bytes数组
iOS中FlatBuffers的使用_第16张图片
@"男"的bytes数组

到了这里我们发现,"张三"、"男"数据都在 flatbuffers 中,并且也在数据内容这边,与官网描述的原理是一致的。

但是flatc生成的OC对象时候是如何分配存储空间的,如何确定"支点"、"元数据"、"数据内容"的暂时还没有深入研究,此处也只是简单的了解下原理。期待各位更深入的研究。

你可能感兴趣的:(iOS中FlatBuffers的使用)