Swift懒加载UITableview出现EXC BAD ACCESS 错误

115 阅读3分钟

场景:在一个页面中使用懒加载添加两个TableView

我们先用一个TableView作为例子来看

懒加载代码 (在里面设置了一些基础属性和代理)

private lazy var ATabView: UITableView = {

        let tableView = UITableView.init()

        if tableView.style == .grouped{

            tableView.sectionHeaderHeight = 0.0

            tableView.sectionFooterHeight = 0.0

            tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0.001))

            tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0.001))

        }

        tableView.showsVerticalScrollIndicator = false

        tableView.delegate = self

        tableView.dataSource = self

        tableView.backgroundColor = UIColor.white

        tableView.estimatedRowHeight = 0

        tableView.estimatedSectionFooterHeight = 0

        tableView.estimatedSectionHeaderHeight = 0

        tableView.separatorStyle = .none

        tableView.tableFooterView = UIView()

        return tableView

    }()

ViewDidLoad中

ATabView.frame = .init(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.view.addSubview(ATabView)

代理方法中的代码

func numberOfSections(in tableView: UITableView) -> Int {

   return 10

}

我们这样使用,程序并不会出现什么问题,但是如果我们用同样懒加载的方法,再添加一个BTableView到视图中。并修改代理方法中的代码用于区分两个不同的UITableView

func numberOfSections(in tableView: UITableView) -> Int {

  if tableView == ATabView {

     return 1

  }else {

     return 2

   }

}

这时候问题就来了,运行后程序卡死,直至报出EXC BAD ACCESS的错误。原因是在懒加载中,我们设置代理的位置不对,当我们在懒加载中设置完ATabView的代理后,又修改了一些属性,导致在ATabView还没创建完成时,执行了代理方法。而在代理方法中,又使用了ATabView,这样在代理方法中使用到的ATabView为空,又会执行懒加载。形成了无限递归的问题。

解决办法

在懒加载中创建ATabView时,需要将所有的属性设置完成后,最后再设置代理。这样代理方法不会提前执行,确保了在代理方法中用到ATabView时,他是被创建好的。这样就不会无限递归了。

另一种情况

多数情况下,我们可能会给UITableView加一个扩展,方便我们每次创建UITableview对象

public extension UITableView {

   static func initCommonTableView(style: UITableView.Style = UITableView.Style.plain, dataSourceDelegate: NSObject, tableViewDelegate: NSObject) -> UITableView {

    let tableView = UITableView.init(frame: .zero, style: style)

    if tableView.style == .grouped{

        tableView.sectionHeaderHeight = 0.0

        tableView.sectionFooterHeight = 0.0

        tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0.001))

        tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0.001))

    }else {

        tableView.tableFooterView = UIView()

    }

    tableView.showsVerticalScrollIndicator = false

    tableView.backgroundColor = UIColor.white

    tableView.estimatedRowHeight = 0

    tableView.estimatedSectionFooterHeight = 0

    tableView.estimatedSectionHeaderHeight = 0

    tableView.separatorStyle = .none

    tableView.delegate = tableViewDelegate as? UITableViewDelegate

    tableView.dataSource = dataSourceDelegate as? UITableViewDataSource

    return tableView

    }

}

在懒加载调用的时候

private lazy var ATabView: UITableView = {

        let tableView = UITableView.initCommonTableView(dataSourceDelegate: self, tableViewDelegate: self)
        return tableView

}()

问题: 如果这时候我们在懒加载中修改tableview的属性,并在代理方法中使用ATabView,还会出现之前无限递归调用懒加载的问题吗?

private lazy var ATabView: UITableView = {

        let tableView = UITableView.initCommonTableView(dataSourceDelegate: self, tableViewDelegate: self)

        tableView.tableFooterView = UIView.init(frame: .init(x: 0, y: 0, width: SCREEN_WIDTH, height: 80))

        return tableView
 }()

经过尝试后发现,并没有不会出现这种问题。为什么呢?我们在扩展中最后的代码设置了代理,然后在懒加载中又修改了tableviewtableFooterView属性,为什么不会无限递归呢?

我们尝试在扩展方法中,注释掉给tableviewtableFooterView赋值的操作,再执行,发现再次出现了无限递归的情况。

结论

tableview设置代理后,再次修改属性时,需要看这个属性,在设置代理前是否被赋值过。如果有,则不会触发代理事件。如果没有,则会触发代理事件。

总之我们在懒加载中给tableview设置代理后,尽可能不要再对tableview进行属性设置。如果需要设置属性,把设置操作放在懒加载之外,也就是ATabview对象被创建后再执行ATabview的属性设置

参考文章

使用Swift懒加载需要注意的陷阱