MMKV的编码和解码

MMKV实现了一套编解码方法,除了引用了protocolbuf对基本类型的编码外,也实现了一些对OC类型的编码。

编码的类型的结构

enum MiniPBEncodeItemType {
    PBEncodeItemType_None,
    PBEncodeItemType_NSString,
    PBEncodeItemType_NSData,
    PBEncodeItemType_NSDate,
    PBEncodeItemType_NSContainer,
};

struct MiniPBEncodeItem {
    MiniPBEncodeItemType type;  // 类型
    int32_t compiledSize;   // (data长度+data内容)的长度
    int32_t valueSize;  //  data内容的长度
    union {
        void *objectValue;  
        /*
        NSData *buffer = [str dataUsingEncoding:NSUTF8StringEncoding];
		encodeItem->value.tmpObjectValue = (__bridge_retained void *) buffer;
		因为在使用tmpObjectValue是buffer在超出作用域后会在runloop睡眠前由autoreleasepool释放,所以要添加引用计数(__bridge_retained void*)
        */
        void *tmpObjectValue; // this object should release on dealloc
    }value ;
}
复制代码

存储方式概述

kv的存储方式

对OC中类型编码的支持

NSData(所有对象都用该形式存储)
void MiniCodedOutputData::writeData(NSData *value) {
	this->writeRawVarint32((int32_t) value.length); // 将长度写入
	this->writeRawData(value);  // 将数据写入
}
复制代码

在函数的命名上raw代表内容本身的bits, 例如writeRawData;writeData代表写入了compiledSize+内容bits。

这是一个NSData的编码

NSString
// MiniCodedOutputData.mm

void MiniCodedOutputData::writeString(NSString *value) {
	NSUInteger numberOfBytes = [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; // utf8编码的长度
	this->writeRawVarint32((int32_t) numberOfBytes);    // 将长度写入
	// 在长度后面写上字符串的utf8字节流
	[value getBytes:m_ptr + m_position  
	         maxLength:numberOfBytes
	        usedLength:0
	          encoding:NSUTF8StringEncoding
	           options:0
	             range:NSMakeRange(0, value.length)
	    remainingRange:nullptr];    
	m_position += numberOfBytes;
}
复制代码

NSString的编码如下,长度使用protobuf提供的Varint编码,内容部分使用utf8编码

这是一个NSString的编码

容器类型
// 这个方法有些混乱,感觉好像有点像个半成品
// 支持[String: Data],[String: String],类型容器或是嵌套的这几种类型容器的编码
// 支持NSString,NSData的完整编码方案,对于NSDate的编码需要依靠外部的一些实现
// 对于其他类型dic是不支持的
- (size_t)prepareObjectForEncode:(NSObject *)obj {
	if (!obj) {
		return m_encodeItems->size();
	}
	m_encodeItems->push_back(MiniPBEncodeItem());
	MiniPBEncodeItem *encodeItem = &(m_encodeItems->back());
	size_t index = m_encodeItems->size() - 1;
    
	if ([obj isKindOfClass:[NSString class]]) {
		NSString *str = (NSString *) obj;
		encodeItem->type = PBEncodeItemType_NSString;
		NSData *buffer = [str dataUsingEncoding:NSUTF8StringEncoding];
		encodeItem->value.tmpObjectValue = (__bridge_retained void *) buffer;
		encodeItem->valueSize = static_cast<int32_t>(buffer.length);
	} else if ([obj isKindOfClass:[NSDate class]]) {
		NSDate *oDate = (NSDate *) obj;
		encodeItem->type = PBEncodeItemType_NSDate;
		encodeItem->value.objectValue = (__bridge void *) oDate;
		encodeItem->valueSize = pbDoubleSize(oDate.timeIntervalSince1970);
		encodeItem->compiledSize = encodeItem->valueSize;
		return index; // double has fixed compilesize
	} else if ([obj isKindOfClass:[NSData class]]) {
		NSData *oData = (NSData *) obj;
		encodeItem->type = PBEncodeItemType_NSData;
		encodeItem->value.objectValue = (__bridge void *) oData;
		encodeItem->valueSize = static_cast<int32_t>(oData.length);
	} else if ([obj isKindOfClass:[NSDictionary class]]) {  // 这里是将容器中的元素按顺序放入vector中,容器编码相关的工作
		encodeItem->type = PBEncodeItemType_NSContainer;
		encodeItem->value.objectValue = nullptr;

		[(NSDictionary *) obj enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
			NSString *nsKey = (NSString *) key; // assume key is NSString
			if (nsKey.length <= 0 || value == nil) {
				return;
			}
#ifdef DEBUG
			if (![nsKey isKindOfClass:NSString.class]) {
				MMKVError(@"NSDictionary has key[%@], only NSString is allowed!", NSStringFromClass(nsKey.class));
			}
#endif

			size_t keyIndex = [self prepareObjectForEncode:key];
			if (keyIndex < self->m_encodeItems->size()) {
				size_t valueIndex = [self prepareObjectForEncode:value];
				if (valueIndex < self->m_encodeItems->size()) {
					(*self->m_encodeItems)[index].valueSize += (*self->m_encodeItems)[keyIndex].compiledSize;
					(*self->m_encodeItems)[index].valueSize += (*self->m_encodeItems)[valueIndex].compiledSize;
				} else {
					self->m_encodeItems->pop_back(); // pop key
				}
			}
		}];

		encodeItem = &(*m_encodeItems)[index];
	} else {
		m_encodeItems->pop_back();
		MMKVError(@"%@ not recognized as container", NSStringFromClass(obj.class));
		return m_encodeItems->size();
	}
	encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;

	return index;
}
复制代码

dic的存储方式

解码过程

每次进行内存映射时(loadFromFile),都会用decode方法,过程如下:

- (NSMutableDictionary *)decodeOneDictionaryOfValueClass:(Class)cls {
	if (cls == nullptr) {
		return nil;
	}

	NSMutableDictionary *dic = [NSMutableDictionary dictionary];

	m_inputData->readInt32();  // 忽略掉dic前的dic byte大小的记录

	while (!m_inputData->isAtEnd()) {
		NSString *nsKey = m_inputData->readString();  //  1. 读出长度 2. 根据长度读出数据
		if (nsKey) {
			id value = [self decodeOneObject:nil ofClass:cls];  1. 读出长度 2. 根据长度读出数据
			if (value) {
				[dic setObject:value forKey:nsKey];  // 写入dic,因为是从前到后读,所以后面的值会覆盖前面的
			} else {
				[dic removeObjectForKey:nsKey];
			}
		}
	}

	return dic;
}
复制代码

转载于:https://juejin.im/post/5c8cc149f265da2de165eb4b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值