【问题标题】:Strong reference cycle: closures vs methods强引用循环:闭包 vs 方法
【发布时间】:2014-10-26 13:14:28
【问题描述】:

我有一个项目,其中包含一个名为 TableViewControllerUITableViewController。因为我希望我的 UITableViewDataSource 协议声明位于我的 TableViewController 声明之外,所以我设置了以下代码(受 Objc.io Lighter View Controllers 启发):

TableViewController:

class TableViewController: UITableViewController {

    let array = [["1"], ["2", "3", "4"], ["5", "6"]]
    var dataSource: DataSource!


    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = DataSource(array: array, configureCellBlock: { (cell, item) in
            cell.textLabel.text = item
        })
        tableView.dataSource = dataSource
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    deinit {
        println("Quit TVC")
    }

}

数据源:

class DataSource: NSObject, UITableViewDataSource {

    let array: [[String]]
    typealias TableViewCellConfigureBlock = (cell: UITableViewCell, item: String) -> ()
    var configureCellBlock: TableViewCellConfigureBlock


    init(array: [[String]], configureCellBlock: TableViewCellConfigureBlock) {
        self.array = array
        self.configureCellBlock = configureCellBlock

        super.init()
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return array.count
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return array[section].count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

        let data = array[indexPath.section][indexPath.row]
        configureCellBlock(cell: cell, item: data)

        return cell
    }

    deinit {
        println("Quit DataSource")
    }

}

这很好用。但是现在,我想用一个方法替换 configureCellBlock 闭包。所以我将我的TableViewController 代码更改为:

class TableViewController: UITableViewController {

    let array = [["1"], ["2", "3", "4"], ["5", "6"]]
    var dataSource: DataSource!


    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = DataSource(array: array, configureCellBlock: formatCell)
        tableView.dataSource = dataSource
    }

    func formatCell(cell: UITableViewCell, item: String) -> () {
        cell.textLabel.text = item
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    deinit {
        println("Quit TVC")
    }

}

现在的问题很明显:如果我运行这段代码,TableViewControllerDataSource 永远不会因为强引用循环而被释放。

我一直在尝试将我的 dataSource 声明更改为 weak var dataSource: DataSource!unowned var dataSource: DataSource,但我最近的尝试都没有奏效。

如何用方法替换我的 configureCellBlock 闭包?我是否必须使用协议委托模式才能这样做?它会是什么样子?

【问题讨论】:

    标签: ios methods swift closures


    【解决方案1】:

    问题在于对formatCell 的引用隐含了对self 的引用。这不是通过使数据源变弱来解决的(您肯定希望在那里有一个强引用),而是确保数据源中的块变量不会保持对视图控制器的强引用。因此,您需要在闭包的开头添加 [unowned self]

    dataSource = DataSource(array: array) {
        [unowned self] cell, item in
    
        self.formatCell(cell, item: item)
        return
    }
    

    【讨论】:

      【解决方案2】:

      您可以使用这样的委托来实现它:

      @objc protocol TableViewCellConfigurator {
        func dataSource( dataSource: DataSource, configureCell cell: UITableViewCell, item: String)
      }
      
      class DataSource: NSObject, UITableViewDataSource {
        weak var cellConfigurator: TableViewCellConfigurator?
      
        (...)
      
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
          let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
          let data = array[indexPath.section][indexPath.row]
          if let delegate = cellConfigurator {
            cellConfigurator.dataSource( self, configureCell: cell, item: data)
          }
          return cell
        }
      
        (...)
      
      }
      
      
      class TableViewController: UITableViewController: TableViewCellConfigurator {
      
        override func viewDidLoad() {
          super.viewDidLoad()
      
          dataSource = DataSource(array: array, configureCellBlock: formatCell)
          tableView.dataSource = dataSource
          dataSource.cellConfigurator = self
        }
      
        override func dataSource( dataSource: DataSource, configureCell cell: UITableViewCell, item: String) {
          cell.textLabel.text = item
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-02-15
        • 1970-01-01
        • 2015-05-06
        • 2016-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多