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

Swift中Codable类weak属性JSONDecoder解码为nil问题咨询

Why Your Weak friend Property Is Always nil After Decoding

Yep, this approach definitely has an issue—here's why and how to fix it:

The Root Cause

Weak references in Swift don't hold a strong ownership of the object they point to. When JSONDecoder tries to decode the friend property:

  • It creates a new User instance for the friend.
  • But since your friend property is marked weak, there's no strong reference keeping this new instance alive.
  • As soon as the decoding finishes creating the friend instance, ARC immediately deallocates it, leaving your friend property as nil.

Fix Options

Option 1: Switch to a Strong Reference (and Handle Cycles Manually)

If your data model has potential cycles (like User A has friend User B, who also has friend User A), you can keep friend as a strong reference and use manual cleanup or unowned in your business logic to avoid retain cycles. For example:

class User: Codable {
    var firstName: String?
    var lastName: String?
    var friend: User? // Strong reference here
    enum CodingKeys: String, CodingKey {
        case firstName = "first"
        case lastName = "last"
        case friend
    }
    
    // Manually break the cycle when it's no longer needed
    func endFriendship() {
        friend = nil
    }
}

This works for most cases unless you have long-lived objects with persistent cycles—then you'll need a more targeted approach.

Option 2: Custom Decoding to Preserve Weak References

If you must keep friend as weak, you need to first create strong references to all User instances during decoding, then assign the weak references afterward. Here's a practical implementation:

  1. Add a unique identifier (like id) to your User class to map instances.
  2. Customize the init(from:) method to use a shared strong reference store (like a dictionary) to hold all users before assigning weak friend properties.

Example code:

class User: Codable {
    var id: String
    var firstName: String?
    var lastName: String?
    weak var friend: User?
    
    enum CodingKeys: String, CodingKey {
        case id, firstName = "first", lastName = "last", friendId
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(String.self, forKey: .id)
        firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
        lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
        
        // Pull the shared user map from decoder's userInfo
        if let friendId = try container.decodeIfPresent(String.self, forKey: .friendId),
           let userMap = decoder.userInfo[.userMapKey] as? [String: User] {
            friend = userMap[friendId]
        }
    }
}

// Define a custom key for the user map in decoder userInfo
extension CodingUserInfoKey {
    static let userMapKey = CodingUserInfoKey(rawValue: "userMapKey")!
}

// How to use this setup:
let jsonData = // Your JSON data (with `id` and `friendId` fields)
var userMap: [String: User] = [:]

// First: Decode all users and store them in the strong map
let decoder = JSONDecoder()
decoder.userInfo[.userMapKey] = userMap
let users = try decoder.decode([User].self, from: jsonData)
users.forEach { userMap[$0.id] = $0 }

// Now all `friend` weak references point to strongly held instances in the map

This way, all User instances are kept alive by the userMap, so assigning the weak friend property won't result in immediate deallocation.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:29:32