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

Swift两类互相嵌套实例化的循环引用问题及解决方法

解决Swift中两个类互相嵌套实例化的问题

兄弟,你现在的代码会直接触发无限递归初始化的问题——OfferItem创建时会实例化Item,Item初始化又会立刻创建OfferItem,循环往复直接把栈撑爆,肯定跑不起来。给你几个靠谱的解决方案,看你需求选:

方案1:使用可选类型延迟关联

把其中一个类的关联属性改成可选类型(默认nil),初始化时不立刻创建对方实例,之后手动完成双向关联。这种方式灵活可控,适合需要明确控制实例生命周期的场景。

class OfferItem { 
    var _id : Int? = 0 
    var _OfferId : Int? = 0 
    var _ItemId : Int? = 0 
    var _Discount : Int? = 0 
    var _item: Item? // 改为可选类型,默认nil
    
    // 可选初始化方法,方便直接关联Item
    init() {}
    init(with item: Item) {
        self._item = item
        item._Offer = self // 反向关联当前OfferItem实例
    }
    
    // 你的业务方法示例
    func applyDiscount() {
        print("应用折扣:\(_Discount ?? 0)%")
    }
} 

class Item { 
    var _id : Int! = 0 
    var _RateCount : Int? = 0 
    var _Offer: OfferItem? // 改为可选类型,默认nil
    
    // 你的业务方法示例
    func showRating() {
        print("评分次数:\(_RateCount ?? 0)")
    }
}

使用示例:

// 方式1:先创建实例再关联
let item = Item()
let offer = OfferItem()
offer._item = item
item._Offer = offer

// 方式2:用初始化方法直接关联
let item2 = Item()
let offer2 = OfferItem(with: item2)

方案2:延迟初始化(lazy)+ 弱引用避免循环引用

如果希望属性在第一次访问时自动初始化,可以用lazy关键字,同时为了避免循环引用(两个对象互相强引用导致内存泄漏),把其中一个属性设为weak。

class OfferItem { 
    var _id : Int? = 0 
    var _OfferId : Int? = 0 
    var _ItemId : Int? = 0 
    var _Discount : Int? = 0 
    
    // lazy属性:第一次访问时才创建Item实例
    lazy var _item: Item = {
        let newItem = Item()
        newItem._Offer = self // 关联当前OfferItem
        return newItem
    }()
    
    func applyDiscount() {
        print("应用折扣:\(_Discount ?? 0)%")
    }
} 

class Item { 
    var _id : Int! = 0 
    var _RateCount : Int? = 0 
    weak var _Offer: OfferItem? // 用weak修饰,避免循环引用
    
    func showRating() {
        print("评分次数:\(_RateCount ?? 0)")
    }
}

说明:

  • lazy闭包里可以安全引用self,因为闭包是在第一次访问_item时才执行,此时OfferItem已经完成初始化。
  • Item的_Offer用weak修饰,因为OfferItem已经强引用了Item,如果Item再强引用OfferItem,会形成循环引用,导致两个对象永远无法被ARC回收。

方案3:用ID关联替代实例持有(推荐给数据模型)

如果这两个类是数据模型(比如从接口返回的实体),更合理的做法是只持有对方的ID,而不是直接持有实例。需要访问关联对象时,再通过ID从数据源(比如数组、数据库)中查询。这种方式完全避免了初始化循环和内存泄漏问题。

class OfferItem { 
    var _id : Int? = 0 
    var _OfferId : Int? = 0 
    var _ItemId : Int? = 0 // 只存Item的ID,不存实例
    var _Discount : Int? = 0 
    
    // 根据ID获取关联的Item实例
    func getAssociatedItem() -> Item? {
        // 假设你有一个全局数据源可以通过ID查询Item
        return DataStore.shared.getItem(by: _ItemId)
    }
    
    func applyDiscount() {
        print("应用折扣:\(_Discount ?? 0)%")
    }
} 

class Item { 
    var _id : Int! = 0 
    var _RateCount : Int? = 0 
    var _OfferId: Int? // 只存OfferItem的ID,不存实例
    
    // 根据ID获取关联的OfferItem实例
    func getAssociatedOffer() -> OfferItem? {
        return DataStore.shared.getOfferItem(by: _OfferId)
    }
    
    func showRating() {
        print("评分次数:\(_RateCount ?? 0)")
    }
}

// 模拟全局数据源
class DataStore {
    static let shared = DataStore()
    private var offerItems: [Int: OfferItem] = [:]
    private var items: [Int: Item] = [:]
    
    func saveOfferItem(_ offer: OfferItem) {
        guard let id = offer._id else { return }
        offerItems[id] = offer
    }
    
    func saveItem(_ item: Item) {
        items[item._id] = item
    }
    
    func getItem(by id: Int?) -> Item? {
        guard let id = id else { return nil }
        return items[id]
    }
    
    func getOfferItem(by id: Int?) -> OfferItem? {
        guard let id = id else { return nil }
        return offerItems[id]
    }
}

使用示例:

let item = Item()
item._id = 1001
item._RateCount = 50
DataStore.shared.saveItem(item)

let offer = OfferItem()
offer._id = 2001
offer._ItemId = 1001
offer._Discount = 20
DataStore.shared.saveOfferItem(offer)

// 通过ID获取关联对象
if let associatedItem = offer.getAssociatedItem() {
    associatedItem.showRating()
}
if let associatedOffer = item.getAssociatedOffer() {
    associatedOffer.applyDiscount()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:48:59