
1 Plist 存储格式Plist文件有3种存储格式:xmlJSON二进制使用plutil -convert fmt命令可以在这3种格式之间相互转换。fmt分别对应上面3种存储格式:xml1jsonbinary1同时如果输出到控制台fmt还可以选择是按照OC还是Swift的方式进行输出:// 按照 OC 输出MacBook-Pro:~ user$ plutil -convert objc -o - xxxx.plist/// Generated from xxxx.plist__attribute__((visibility(hidden)))NSDictionary * const xxxx {BuildVersion : 50,CFBundleShortVersionString : 4.13,CFBundleVersion : 3146.1.2,ProjectName : Notes,SourceVersion : 3146001002000000,};// 按照 Swift 输出MacBook-Pro:~ user$ plutil -convert swift -o - xxxx.plist/// Generated from xxxx.plistlet xxxx [BuildVersion : 50,CFBundleShortVersionString : 4.13,CFBundleVersion : 3146.1.2,ProjectName : Notes,SourceVersion : 3146001002000000,]上面命令中的-o -表示要输出到控制台。2 Plist 二进制格式Plist二进制格式描述在苹果CF代码库中的CFBinaryPList.c中。Plist二进制格式整体如下:从图上可以看到Header和Trailer的大小是固定的。Objects和Offset-Table部分是可变的。下面给出了一个二进制Plist的内容相关部分已经用不同颜色进行了区分:将二进制Plist打印为xml输出到控制台的内容为:MacBook-Pro:~ user$ plutil -convert xml1 -o - xxxx.plist?xml version1.0 encodingUTF-8?!DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtdplist version1.0dictkeyBuildVersion/keystring50/stringkeyCFBundleShortVersionString/keystring4.13/stringkeyCFBundleVersion/keystring3146.1.2/stringkeyProjectName/keystringNotes/stringkeySourceVersion/keystring3146001002000000/string/dict/plist可以将xml内容和二进制内容对照着看。2.1 HeaderHeader总共有8字节定义如下:// ForFoundationOnly.htypedef struct {uint8_t _magic[6];uint8_t _version[2];} CFBinaryPlistHeader;2.1.1 magicmagic固定为plist。2.1.2 versionplist常见的version是00。同时还有15和16。版本15和16都没有文档说明。2.2 TrailerTrailer固定位于二进制Plist的尾部总共32字节。Trailer的定义如下:// ForFoundationOnly.htypedef struct {uint8_t _unused[5];uint8_t _sortVersion;uint8_t _offsetIntSize;uint8_t _objectRefSize;uint64_t _numObjects;uint64_t _topObject;uint64_t _offsetTableOffset;} CFBinaryPlistTrailer;2.2.1 UnusedTrailer开始的头5字节没有使用始终是0。2.2.2 SortVersionSortVersion表是Plist中的Key是否排序占用1字节。0表示未排序。2.2.3 OffsetIntSizeOffsetIntSize表示Offset-Table中每一项占用的字节数。在上面例子中可以看到OffsetIntSize为1表示Offset-Table中每项只占用1字节。2.2.4 ObjectRefSizeObjectRefSize表示Dict和Array中引用的对象索引占用的字节数。在上面例子中ObjectRefSize为1表示Dict和Array中引用对象的索引占用1字节。以Objects区域的第1个字节d5为例。d5的高4bit代表数据类型d代表Dict。d5的低4bit在Dict场景代表key-value对的个数也就是这个Dict有5个key-value对。d5后面0x01-0x05每1个字节代表key在Offset-Table中的索引。0x06-0x0a每1个字节代表value在Offset-Table中的索引。如果ObjectRefSize为2那么这些索引就会占用2个字节。2.2.5 Num-ObjectsNum-Objects代表二进制Plist中的对象个数。在上面例子中有一个Dict这个Dict有5个key-value对因此总共有11个对象。2.2.6 Top-ObjectTop-Object表示根对象偏移量一般都为0。在上面例子中根对象就是plist节点。2.2.7 Offset-Table OffsetOffset-Table Offset表示Offset-Table的偏移量。在上面例子中Offset-Table位于0x93处。2.2.8 Offset-TableOffset-Table中的每一项存储对象在Plist文件中的偏移量。Offset-Table的项从0开始计数。上面例子中Offset-Table的第0项为0x08。Plist二进制偏移0x08处的值是d5。d5代表了数据类型。d5高4bit代表这是一个Dict。d5低4bit代表这个Dict有5个key-value对。2.2.9 ObjectsObjects区域就是二进制Plist的数据区。3 Plist 二进制中的数据类型常见的Plist二进制中的数据类型如下:0x000x00代表null。0x080x08代表布尔值false。0x090x09代表布尔值true。0x1n0x1n表示是int类型。0x1n中的n可以取0~7表示这个int占用2^n个字节这些字节是大端在前排列。比如0x11 0x20 0x30那么n1表示这个int占用2个字节为0x2030。0x2n0x2n表示是real类型。0x2n中的n可以取值0~7表示这个real类型占用2^n个字节这些字节是大端在前排列。0x5n0x5n表示ASCII字符串。0x5n的n取值0-f表示这个ASCII字符串占用的字节数。ASCII字符串的内容接在0x5n的后面。但是如果nf那么后面的1个字节表示一个int类型定义了这个ASCII字符串占用的字节数。比如有一个字节序列0x5f 10 0f。0x5f表示这是一个ASCII字符串。10按照上面int的定义它后面的2^01个字节定义了这个整数也就是0xf。那么整个ASCII字符串就占用0xf个字节。anan表示是Array类型。an的n取值0-f表示这个Array中元素的个数。Array中每个元素在Offset-Table中的索引直接接在an后面。如果nf那么就和0x5n中的一样。dndn表示是Dict类型。dn的n取值0-f表示这个Dict的key-value对个数。Dict后面先跟key在Offset-Table中的索引接着跟着value在Offset-Table中的索引。如果nf那么就和0x5n中的一样