使用User Default触发NSInvalidArgumentException异常,求修复方案
解决UserDefaults的「Attempt to insert non-property list object」异常
这个错误我太熟了——本质是UserDefaults只支持存储苹果官方定义的「属性列表(Property List)类型」,如果你硬塞自定义对象、Swift结构体、UIImage这类非标准类型进去,就会触发这个崩溃。
先明确哪些类型可以直接存进UserDefaults:
- 基础类型:
NSString/String、NSNumber/Int/Float/Bool、NSDate/Date - 集合类型:
NSArray/Array(里面元素必须也是属性列表类型)、NSDictionary/Dictionary(键值都得是属性列表类型) - 二进制数据:
NSData/Data
针对不同场景的解决方案:
场景1:存储自定义类/结构体
最现代的做法是用Swift的Codable协议把对象转成Data再存储,步骤如下:
- 让你的自定义类型遵循
Codable协议(无需额外实现方法,只要属性都是Codable类型) - 用
JSONEncoder把对象编码成Data - 把
Data存进UserDefaults;读取时用JSONDecoder解码回原对象
示例代码:
// 自定义User类,遵循Codable class User: Codable { var username: String var avatarURL: String init(username: String, avatarURL: String) { self.username = username self.avatarURL = avatarURL } } // 存储操作 let currentUser = User(username: "BoL", avatarURL: "https://example.com/avatar.png") do { let encodedData = try JSONEncoder().encode(currentUser) UserDefaults.standard.set(encodedData, forKey: "CurrentUser") } catch { print("编码失败:\(error.localizedDescription)") } // 读取操作 if let savedData = UserDefaults.standard.data(forKey: "CurrentUser") { do { let savedUser = try JSONDecoder().decode(User.self, from: savedData) print("读取到用户:\(savedUser.username)") } catch { print("解码失败:\(error.localizedDescription)") } }
场景2:存储UIImage这类特殊对象
UIImage不属于属性列表类型,你可以先把它转成PNG/JPG格式的Data再存储:
// 存储UIImage if let avatarImage = UIImage(named: "avatar"), let imageData = avatarImage.pngData() { UserDefaults.standard.set(imageData, forKey: "UserAvatar") } // 读取UIImage if let savedImageData = UserDefaults.standard.data(forKey: "UserAvatar"), let savedImage = UIImage(data: savedImageData) { // 使用savedImage }
场景3:OC项目中的自定义对象
如果是Objective-C项目,需要让类遵循NSCoding协议,实现归档和解档方法:
// User.h #import <Foundation/Foundation.h> @interface User : NSObject <NSCoding> @property (nonatomic, copy) NSString *username; @property (nonatomic, assign) NSInteger age; @end // User.m #import "User.h" @implementation User - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.username forKey:@"username"]; [coder encodeInteger:self.age forKey:@"age"]; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { self.username = [coder decodeObjectForKey:@"username"]; self.age = [coder decodeIntegerForKey:@"age"]; } return self; } @end // 存储 User *user = [[User alloc] init]; user.username = @"BoL"; user.age = 28; NSData *userData = [NSKeyedArchiver archivedDataWithRootObject:user requiringSecureCoding:YES error:nil]; [[NSUserDefaults standardUserDefaults] setObject:userData forKey:@"CurrentUser"]; // 读取 NSData *savedData = [[NSUserDefaults standardUserDefaults] objectForKey:@"CurrentUser"]; User *savedUser = [NSKeyedUnarchiver unarchivedObjectOfClass:[User class] fromData:savedData error:nil];
快速排查小技巧
如果不确定自己要存的对象是不是合法类型,可以先打印类型确认:
let yourObject = // 你要存的对象 print("对象类型:\(type(of: yourObject))")
如果输出不是上面列的合法类型,就需要先转成Data再存储。
内容的提问来源于stack exchange,提问作者Bo L
相关产品推荐
相关产品推荐

