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

iOS开发:Plist数组字典中布尔值修改与持久化问题

解决Plist修改收藏状态的问题

首先得拆解你当前代码里的几个核心问题:

1. Bundle内的文件是只读的,无法直接写入

你现在直接操作Bundle.main.path拿到的plist路径,这个路径下的文件是打包在App安装包内的,完全只读,根本没法修改保存。必须先把原始plist复制到App的Documents目录,之后所有的读写操作都针对这个副本。

2. NSArray/NSMutableDictionary的使用错误

你把NSArray(contentsOfFile:)强制转成[NSMutableDictionary],但NSArray本身是不可变容器,转出来的数组里的字典实际还是不可变的NSDictionary,所以调用setValue会报错。而且你直接给数组dics调用setValue,这逻辑完全不对——你要修改的是用户点击的那一个动物对应的字典,不是整个数组。

完整解决方案

第一步:封装Plist读写工具(避免重复代码)

写一个单例工具类,负责完成plist的复制、读取和保存:

import Foundation

class PlistManager {
    static let shared = PlistManager()
    
    private let plistName = "Animals"
    private var documentPlistPath: String {
        let documentsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        return "\(documentsDir)/\(plistName).plist"
    }
    
    // 初始化时自动把Bundle里的plist复制到Documents(仅当副本不存在时)
    private init() {
        let bundlePath = Bundle.main.path(forResource: plistName, ofType: "plist")!
        if !FileManager.default.fileExists(atPath: documentPlistPath) {
            do {
                try FileManager.default.copyItem(atPath: bundlePath, toPath: documentPlistPath)
            } catch {
                print("复制plist失败:\(error)")
            }
        }
    }
    
    // 读取所有动物字典
    func loadAnimals() -> [NSMutableDictionary] {
        guard let dics = NSMutableArray(contentsOfFile: documentPlistPath) as? [NSMutableDictionary] else {
            return []
        }
        return dics
    }
    
    // 保存修改后的动物数组
    func saveAnimals(_ animals: [NSMutableDictionary]) -> Bool {
        let array = NSMutableArray(array: animals)
        return array.write(toFile: documentPlistPath, atomically: true)
    }
}

第二步:ViewController1的修改

用工具类加载数据,并传递选中动物的索引到ViewController2:

// ViewController1内
var animalDics: [NSMutableDictionary] = []

override func viewDidLoad() {
    super.viewDidLoad()
    animalDics = PlistManager.shared.loadAnimals()
    // 后续映射为Animals模型数组,刷新TableView
}

// TableView点击事件
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let vc = ViewController2()
    // 传递选中动物的索引(如果动物有唯一id字段,传递id会更稳妥)
    vc.selectedAnimalIndex = indexPath.row
    navigationController?.pushViewController(vc, animated: true)
}

第三步:ViewController2的修改

接收索引、找到对应字典、修改收藏状态并保存:

// ViewController2内
var selectedAnimalIndex = 0
var isFavorite = false
@IBOutlet weak var favButton: UIButton!

// 收藏按钮点击事件
@IBAction func favButtonTapped(_ sender: UIButton) {
    isFavorite.toggle()
    sender.isSelected = isFavorite
    
    // 获取所有动物字典
    var animalDics = PlistManager.shared.loadAnimals()
    guard selectedAnimalIndex < animalDics.count else { return }
    // 修改选中动物的"fav"值
    let selectedDic = animalDics[selectedAnimalIndex]
    selectedDic.setValue(isFavorite, forKey: "fav")
    
    // 保存回Documents目录的plist
    let success = PlistManager.shared.saveAnimals(animalDics)
    print(success ? "收藏状态保存成功" : "保存失败")
    
    // 通知ViewController1刷新数据(可选)
    NotificationCenter.default.post(name: NSNotification.Name("AnimalFavUpdated"), object: nil)
}

// 页面加载时初始化按钮状态
override func viewDidLoad() {
    super.viewDidLoad()
    let animalDics = PlistManager.shared.loadAnimals()
    guard selectedAnimalIndex < animalDics.count else { return }
    let selectedDic = animalDics[selectedAnimalIndex]
    isFavorite = selectedDic.value(forKey: "fav") as? Bool ?? false
    favButton.isSelected = isFavorite
}

第四步:ViewController1监听数据刷新(可选)

在ViewController1中添加通知监听,收到更新通知后重新加载数据:

override func viewDidLoad() {
    super.viewDidLoad()
    // ... 其他初始化代码
    NotificationCenter.default.addObserver(self, selector: #selector(refreshAnimalData), name: NSNotification.Name("AnimalFavUpdated"), object: nil)
}

@objc func refreshAnimalData() {
    animalDics = PlistManager.shared.loadAnimals()
    // 重新映射模型数组并刷新TableView
    tableView.reloadData()
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

关于之前UserDefaults的问题补充

你提到用UserDefaults时所有动物的布尔值同时变更,大概率是因为没给每个动物设置唯一的存储key。比如如果每个动物有唯一的id字段,存储时可以用"animal_fav_\(animalId)"作为key,这样修改时只会影响对应动物的状态,不会波及其他。如果追求更轻量的存储方案,UserDefaults其实是个不错的选择,只要做好key的区分即可。

内容的提问来源于stack exchange,提问作者Dkeem

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:04:36