背景
在iOS中数据的存储,无论是存储数据库(sqlite 支持的数据类型有五种NULL,整形(INT),浮点型(REAL)文本字符串(TEXT),二进制数据(BLOB) )还是存放在keychain , 或者是本地 plist 的文件 ,数据都以NSData(二进制数据)进行存储比较好,这样就牵扯到我们每定义一个新的类型,如果该数据需要义二进制数据进行存储时,必须进行归档和解档 ,每一次都要每定义一个新类都要进行一次归档和解档的书写,那有没有通用的办法呢?
解决方案
上面的问题我们使用runtime来解决,在运行时,动态的获取该类是否遵循NSCoding 协议,动态的遵循该协议,并实现该协议的归档解档方法,如题流程如图:
如题代码如下:
利用runtime动态为一个类添加NSCoding协议和NSCoding协议对应的方法
- + (void)addCodingProtocolToObject:(Class )class
- {
- SEL encodeSEL = @selector(encodeWithCoder:);
- SEL decoderSEL = @selector(initWithCoder:);
- [DataManager addProtocol:@"NSCoding" toClass:class];
- Class destClass = [DataManager class];
- [DataManager addProtocolMethod:encodeSEL withClass:destClass toClass:class];
- [DataManager addProtocolMethod:decoderSEL withClass:destClass toClass:class];
-
}
利用runtime 动态的判断某一个类是否遵循了某一个协议方法,如果没有遵循,则动态的遵循
- + (void)addProtocol:(NSString *)protocolName toClass:(Class)class
- {
- if([protocolName hasPrefix:@"<"])
- {
- protocolName = [protocolName substringWithRange:NSMakeRange(1, protocolName.length-2)];
- }
- const char *name = [protocolName UTF8String];
- Protocol *instanceProtocol = objc_getProtocol(name);
- if(instanceProtocol)
- {
- if(class_conformsToProtocol(class, instanceProtocol))
- {
- return;
- }
- }else
- {
- instanceProtocol = objc_allocateProtocol(name);
- objc_registerProtocol(instanceProtocol);
- }
- class_addProtocol(class, instanceProtocol);
- }
利用 runtime 动态为某一个类添加另一个类的某一个方法
- + (void)addProtocolMethod:(SEL)protocolSEL withClass:(Class)selClass toClass:(Class)toClass
- {
- Method protocolMethod = class_getInstanceMethod(selClass,protocolSEL);
- IMP protocolIMP = method_getImplementation(protocolMethod);
- const char *protocolTypes = method_getTypeEncoding(protocolMethod);
- if(!class_addMethod(toClass, protocolSEL, protocolIMP, protocolTypes))
- {
- class_replaceMethod(toClass, protocolSEL, protocolIMP, protocolTypes);
- }
- }
利用runtime 动态的获取某一个类属性,包括继承自父类的属性,进行归档
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- Class selfClass = self.class;
- while (selfClass &&selfClass != [NSObject class]) {
- unsigned int outCount = 0;
- Ivar *ivars = class_copyIvarList([selfClass class], &outCount);
- for (int i = 0; i < outCount; i++) {
- Ivar ivar = ivars[i];
- const char *name = ivar_getName(ivar);
- NSString *key = [NSString stringWithUTF8String:name];
- id value = [self valueForKeyPath:key];
- [aCoder encodeObject:value forKey:key];
- }
- free(ivars);
- selfClass = [selfClass superclass];
- }
- }
利用runtime 动态的获取某一个类的属性,包括继承自父类的属性,进行解档
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- Class selfClass = self.class;
- while (selfClass &&selfClass != [NSObject class]) {
- unsigned int outCount = 0;
- Ivar *ivars = class_copyIvarList(selfClass, &outCount);
- for (int i = 0; i < outCount; i++) {
- Ivar ivar = ivars[i];
- NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
- id value = [aDecoder decodeObjectForKey:key];
- [self setValue:value forKey:key];
- }
- free(ivars);
- selfClass = [selfClass superclass];
- }
- return self;
- }
优点:
以后对任何一个对象转化为NSData 进行存储和传输时,不用每次去手动的实现归档和解档,只用调用该方法即可
+ (void)addCodingProtocolToObject:(Class )class 传入类名
限制:
由于归档解档本身不支持可变的集合进行转化为二进制数据存储,所以改方案也不支持可变的集合 NSMutableArray NSMutableDictionary的类的归档和解档