iOS Swift中持久化自定义对象数组的更优实现方案咨询
更优的Plant数组编码解码方案
嘿,你说得对——专门为了编码数组创建一个实现NSCoder的单例类确实有点冗余,这里有两个更简洁的方案,看哪个更适合你的场景:
方案一:让Plant类直接遵守NSCoding协议(推荐)
最符合面向对象设计的方式,就是让Plant自己负责自身的编码和解码逻辑,毕竟它最清楚自己的属性。这样你后续不管是单独存一个Plant还是存数组,都能直接用NSKeyedArchiver/NSKeyedUnarchiver,完全不需要额外的类。
举个OC的代码例子:
Plant.h
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface Plant : NSObject <NSCoding> @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *species; @property (nonatomic, strong) UIImage *icon; @end
Plant.m
#import "Plant.h" @implementation Plant // 编码方法 - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.name forKey:@"name"]; [coder encodeObject:self.species forKey:@"species"]; // UIImage转成NSData再编码,避免直接编码可能出现的问题 NSData *imageData = UIImagePNGRepresentation(self.icon); [coder encodeObject:imageData forKey:@"iconData"]; } // 解码方法 - (nullable instancetype)initWithCoder:(NSCoder *)coder { if (self = [super init]) { self.name = [coder decodeObjectForKey:@"name"]; self.species = [coder decodeObjectForKey:@"species"]; NSData *imageData = [coder decodeObjectForKey:@"iconData"]; self.icon = [UIImage imageWithData:imageData]; } return self; } @end
之后存数组就非常简单了:
// 保存Plant数组 NSArray *plants = @[plant1, plant2]; NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"plants.archive"]; [NSKeyedArchiver archiveRootObject:plants toFile:filePath]; // 读取Plant数组 NSArray *savedPlants = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
方案二:不修改Plant类,直接在编码时处理属性
如果你暂时不想修改Plant类的代码,也可以不用单独创建单例,直接在保存/读取的逻辑里手动处理每个Plant的属性:
保存逻辑
NSMutableArray *encodedPlants = [NSMutableArray array]; for (Plant *plant in plantsArray) { NSMutableDictionary *plantDict = [NSMutableDictionary dictionary]; plantDict[@"name"] = plant.name; plantDict[@"species"] = plant.species; plantDict[@"iconData"] = UIImagePNGRepresentation(plant.icon); [encodedPlants addObject:plantDict]; } // 把字典数组存起来 NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"plants.plist"]; [encodedPlants writeToFile:filePath atomically:YES];
读取逻辑
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"plants.plist"]; NSArray *savedDicts = [NSArray arrayWithContentsOfFile:filePath]; NSMutableArray *plants = [NSMutableArray array]; for (NSDictionary *dict in savedDicts) { Plant *plant = [[Plant alloc] init]; plant.name = dict[@"name"]; plant.species = dict[@"species"]; plant.icon = [UIImage imageWithData:dict[@"iconData"]]; [plants addObject:plant]; }
这个方案不用改Plant类,但缺点是每次属性变动都要同步修改保存和读取的代码,扩展性不如方案一。
总的来说,方案一是最推荐的——让对象自己管理编码解码,逻辑更清晰,后续维护也更方便,完全不需要额外的单例类来做这件事~
内容的提问来源于stack exchange,提问作者Gabe Spound
相关产品推荐
相关产品推荐

