滚动UITableView时,仪器显示“_NSContiguousstring”内存泄漏
我有一个UItableView,由10个元素组成。 我打开了仪器以捕获内存泄漏,当我滚动tableView时,它开始泄漏内存。 在仪器中我试图找到哪个导致泄漏但无法弄清楚,它说“_NScontiguousstring”表示整个泄漏。
我找到了一些Objective-C的解决方案,他们在“CellForRowAt”函数中检查了Cell是否为nil。 我不认为它对Swift有用,但我尝试了并且按预期它不起作用。
内存泄漏UITableView
我的问题是什么可能导致这种内存泄漏?
我测试的设备;
iPhone X on 11.3.1
iPhone 6 11.2.5控制器类;
class TableViewController: UITableViewController { let teamModel = TeamModel(uid: "adsada", name: "First Team ", idea: "idea 1", slogan: "Slogan 1", university: "dasda", image: "info", isActive: true) let teamModel2 = TeamModel(uid: "adsada", name: "Team 2", idea: "idea 2", slogan: "adasd", university: "dasda", image: "info", isActive: true) let teamModel3 = TeamModel(uid: "adsada", name: "Team 3", idea: "idea 3", slogan: "adasd", university: "dasda", image: "info", isActive: true) let teamModel4 = TeamModel(uid: "adsada", name: "Team 4", idea: "idea 4", slogan: "adasd", university: "dasda", image: "info", isActive: true) let teamModel5 = TeamModel(uid: "adsada", name: "Team 5", idea: "idea 5", slogan: "adasd", university: "dasda", image: "info", isActive: true) let teamModel6 = TeamModel(uid: "adsada", name: "Team 6", idea: "idea 6", slogan: "adasd", university: "dasda", image: "info", isActive: true) let teamModel7 = TeamModel(uid: "adsada", name: "Team 7", idea: "idea 7", slogan: "adasd", university: "dasda", image: "info", isActive: true) var data: [TeamModel] = [] override func viewDidLoad() { super.viewDidLoad() tableView.register(mainTableCell.self, forCellReuseIdentifier: "mainTableCell") data = [teamModel,teamModel2,teamModel3,teamModel4,teamModel5,teamModel6,teamModel7] } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return data.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "mainTableCell", for: indexPath) as! mainTableCell let cell_data = data[indexPath.row] cell.cell_data = cell_data return cell } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 120 } }
细胞类;
class mainTableCell: UITableViewCell{ var cell_data: TeamModel?{ didSet{ guard let unwrappedCell = cell_data else { return } if let url = unwrappedCell.imageURL{ profileImage.image = UIImage(named: "info") } else{ self.profileImage.image = UIImage(named: "info") } self.teamLbl.text = unwrappedCell.name mainBackground.backgroundColor = UIColor.gray } } let mainBackground: UIView = { let v = UIView() v.layer.cornerRadius = 8 v.layer.masksToBounds = true return v }() var isVoteable: Bool = false //let shadowView = ShadowView() let profileImage: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFill imageView.clipsToBounds = true imageView.layer.masksToBounds = true return imageView }() let teamLbl: UILabel = { let label = UILabel() label.textColor = UIColor.black label.numberOfLines = 1 label.adjustsFontSizeToFitWidth = true return label }() let myVoteLbl: UILabel = { let l = UILabel() l.text = "Oyum: --" return l }() let voteBtn: UIButton = { let b = UIButton(type: .custom) b.setImage(UIImage(named: "info"), for: .normal) return b }() override init(style: UITableViewCellStyle, reuseIdentifier: String?){ super.init(style: style, reuseIdentifier: reuseIdentifier) setupViews() } func setupViews() { self.backgroundColor = UIColor.white self.mainBackground.addSubview(profileImage) self.mainBackground.addSubview(teamLbl) self.mainBackground.addSubview(voteBtn) self.mainBackground.addSubview(myVoteLbl) //self.addSubview(shadowView) self.addSubview(mainBackground) self.backgroundColor = UIColor.clear profileImage.translatesAutoresizingMaskIntoConstraints = false teamLbl.translatesAutoresizingMaskIntoConstraints = false voteBtn.translatesAutoresizingMaskIntoConstraints = false myVoteLbl.translatesAutoresizingMaskIntoConstraints = false mainBackground.translatesAutoresizingMaskIntoConstraints = false //shadowView.translatesAutoresizingMaskIntoConstraints = false } override func layoutSubviews() { super.layoutSubviews() // self.setCircularImageView() mainBackground.anchor(self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, topConstant: 10, leftConstant: 10, bottomConstant: 10, rightConstant: 10, widthConstant: 0, heightConstant: 0) // shadowView.anchor(self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, topConstant: 5, leftConstant: 5, bottomConstant: 5, rightConstant: 5, widthConstant: 0, heightConstant: 0) profileImage.anchor(nil, left: self.mainBackground.leftAnchor, bottom: nil, right: nil, topConstant: 0, leftConstant: 10, bottomConstant: 0, rightConstant: 0, widthConstant: 60, heightConstant: 60) profileImage.anchorCenterYToSuperview() teamLbl.anchor(self.mainBackground.topAnchor, left: profileImage.rightAnchor, bottom: nil, right: self.voteBtn.leftAnchor, topConstant: 20, leftConstant: 20, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 40) myVoteLbl.anchor(nil, left: profileImage.rightAnchor, bottom: self.mainBackground.bottomAnchor, right: nil, topConstant: 0, leftConstant: 20, bottomConstant: 20, rightConstant: 0, widthConstant: 0, heightConstant: 0) voteBtn.anchor(nil, left: nil, bottom: nil, right: self.mainBackground.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 80, heightConstant: 80) voteBtn.anchorCenterYToSuperview() } func setCircularImageView() { self.profileImage.layer.cornerRadius = CGFloat(roundf(Float(self.profileImage.frame.size.width / 2.0))) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
编辑:我添加prepareForReuse方法关于下面的答案,但现在细胞显示只是白色。 难道我做错了什么?
override func prepareForReuse() { super.prepareForReuse() teamLbl.text = "" myVoteLbl.text = "" profileImage.image = nil }
我还使cell_data变量“weak var cell_data:TeamModel?并且在prepareForReuse()方法中为零,但仍然会发生相同的泄漏。
编辑2:完整项目;
https://github.com/emreond/TableView-Memory-Leak-Project
编辑3:我发现当我添加If检查并更改cell_data中的属性时,某些单元格视图看起来很糟糕。 例如,当我在cell_data中更改teamLbl文本颜色时。 在某些细胞中,它看起来很糟糕。
编辑4:当我评论锚码以参考@Darp的答案进行检查时,(我没有在屏幕上看到任何内容并且没有任何约束)它仍然继续泄漏。
编辑5:我仍然在努力解决这个漏洞,但找不到任何解决方案。 实际上当我查看tableView在屏幕上时内存中的内容时,我看到只有8个单元格在内存中,看起来是正确的。
我对_NSContiguousString
也有同样的问题。 即使在只有一个标签的空应用程序中,它也会显示48字节的泄漏。
我不确定,但我猜测泄漏的原因可能是与NSString桥接(我在堆栈跟踪的顶部也看到[_SwiftNativeNSStringBase retain]
)。
我发现NSString本身可能被仪器视为泄漏对象 ,因为它已被实现为Singleton。
我自己的问题是: 将文本分配给UILabel(iOS,Swift 4,XCode9)时内存泄漏
首先要做的事情。
在layoutSubviews
里面你正在设置锚点。 为什么? 每次调用细胞时都会调用它。
而不是这个,移动
// self.setCircularImageView() mainBackground.anchor(self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, topConstant: 10, leftConstant: 10, bottomConstant: 10, rightConstant: 10, widthConstant: 0, heightConstant: 0) // shadowView.anchor(self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, topConstant: 5, leftConstant: 5, bottomConstant: 5, rightConstant: 5, widthConstant: 0, heightConstant: 0) profileImage.anchor(nil, left: self.mainBackground.leftAnchor, bottom: nil, right: nil, topConstant: 0, leftConstant: 10, bottomConstant: 0, rightConstant: 0, widthConstant: 60, heightConstant: 60) profileImage.anchorCenterYToSuperview() teamLbl.anchor(self.mainBackground.topAnchor, left: profileImage.rightAnchor, bottom: nil, right: self.voteBtn.leftAnchor, topConstant: 20, leftConstant: 20, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 40) myVoteLbl.anchor(nil, left: profileImage.rightAnchor, bottom: self.mainBackground.bottomAnchor, right: nil, topConstant: 0, leftConstant: 20, bottomConstant: 20, rightConstant: 0, widthConstant: 0, heightConstant: 0) voteBtn.anchor(nil, left: nil, bottom: nil, right: self.mainBackground.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 80, heightConstant: 80) voteBtn.anchorCenterYToSuperview()
只是setupViews()
调用。
接下来就是你没有实现prepareForReuse()
,你需要在cellForRow
,willDislayCell`等中设置你的值。
编辑
var cell_data: TeamModel?
– 将其标记为可选不会使其成为弱引用,将其标记为weak var
并且每次cell
重用时都nil
。