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

如何用NSKeyedArchiver存储枚举与嵌套数组?iOS持久化求助

Hey there! I totally get how tricky iOS persistence can feel when you're just starting out—those crashers and failed saves are so frustrating. Let's walk through fixing the two main issues you're hitting with NSKeyedArchiver: enums breaking saves and nested arrays crashing your IDE.

Enums don't play nice with NSCoding out of the box because they aren't NSObject subclasses. The fix is to use their raw values (like String or Int) to encode/decode them explicitly. Here's a step-by-step example:

First, define your enum with a serializable raw type:

enum ItemStatus: String {
    case active, completed, archived
}

Then, in your custom class that uses this enum, implement NSCoding by converting the enum to its raw value when encoding, and converting back when decoding:

class TodoItem: NSObject, NSCoding {
    var title: String
    var status: ItemStatus

    init(title: String, status: ItemStatus) {
        self.title = title
        self.status = status
    }

    // Encode the enum's raw value instead of the enum itself
    func encode(with coder: NSCoder) {
        coder.encode(title, forKey: "title")
        coder.encode(status.rawValue, forKey: "status")
    }

    // Decode the raw value and safely convert it back to the enum
    required init?(coder: NSCoder) {
        guard let title = coder.decodeObject(forKey: "title") as? String,
              let statusRaw = coder.decodeObject(forKey: "status") as? String,
              let status = ItemStatus(rawValue: statusRaw) else {
            // If any step fails, return nil to avoid invalid objects
            return nil
        }
        self.title = title
        self.status = status
    }
}

The key here is never trying to encode the enum directly—always use its raw value, and add guard clauses to handle cases where decoding might fail (like if the raw value doesn't match any enum case).

Fixing Nested Array IDE Crashes

Crashes when adding nested arrays almost always come down to one of two issues:

  1. The objects inside your nested arrays don't conform to NSCoding properly
  2. Accidental recursive encoding (infinite loops) in your encode(with:) method

Let's fix this with an example. Suppose you have a TodoList class that contains a nested array of TodoItem objects:

class TodoList: NSObject, NSCoding {
    var sections: [[TodoItem]] // Nested array of TodoItem

    init(sections: [[TodoItem]]) {
        self.sections = sections
    }

    func encode(with coder: NSCoder) {
        // Since TodoItem conforms to NSCoding, we can encode the nested array directly
        coder.encode(sections, forKey: "sections")
    }

    required init?(coder: NSCoder) {
        // Safely decode the nested array, defaulting to an empty array if decoding fails
        self.sections = coder.decodeObject(forKey: "sections") as? [[TodoItem]] ?? []
    }
}

Critical Checks to Avoid Crashes:

  • All nested objects must conform to NSCoding: If you have a struct instead of a class, you'll need to make it conform to NSCoding (or wrap it in an NSObject subclass if you're sticking with older iOS versions).
  • Watch for retain cycles: If your objects reference each other (e.g., a TodoItem has a reference to its parent TodoList), use weak references to prevent memory leaks that can lead to crashes during archiving.
  • Avoid recursive encoding: Never call encode(with:) on the same object inside its own encode method—this creates an infinite loop that will crash your IDE or app.

Quick Sanity Check for Your Archive/Unarchive Flow

Make sure your save/load code is set up correctly too—here's a standard implementation:

// Get the path to save your archive
private func getArchiveURL() -> URL {
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    return documentsDirectory.appendingPathComponent("todoList.archive")
}

// Save your data
func saveTodoList(_ list: TodoList) {
    let data = NSKeyedArchiver.archivedData(withRootObject: list, requiringSecureCoding: false)
    do {
        try data.write(to: getArchiveURL())
    } catch {
        print("Save failed with error: \(error.localizedDescription)")
    }
}

// Load your data
func loadTodoList() -> TodoList? {
    guard let data = try? Data(contentsOf: getArchiveURL()),
          let list = NSKeyedUnarchiver.unarchiveObject(with: data) as? TodoList else {
        return nil
    }
    return list
}

Note: Setting requiringSecureCoding to false is fine for testing, but for production apps, you'll want to implement secure coding by conforming your classes to NSSecureCoding.

A quick side note: If you're feeling overwhelmed by Core Data, once you get past these NSKeyedArchiver issues, check out SwiftData (iOS 16+)—it's a much simpler, Swift-native persistence framework that removes a lot of the boilerplate of Core Data.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:47:52