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
相关产品推荐
相关产品推荐

