You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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再存储,步骤如下:

  1. 让你的自定义类型遵循Codable协议(无需额外实现方法,只要属性都是Codable类型)
  2. 用JSONEncoder把对象编码成Data
  3. 把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:53:48