滚动UITableView时,仪器显示"_NSContiguousstring"内存泄漏

Emr*_*der 10 memory-leaks uitableview ios swift

我有一个UItableView,由10个元素组成.我打开了仪器以捕获内存泄漏,当我滚动tableView时,它开始泄漏内存.在仪器中我试图找到哪个导致泄漏但无法弄清楚,它说"_NScontiguousstring"表示整个泄漏.

我找到了Objective-C的一些解决方案,他们在"CellForRowAt"函数中检查了单元格是否为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
}
}
Run Code Online (Sandbox Code Playgroud)

细胞类;

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")
}
}
Run Code Online (Sandbox Code Playgroud)

第一个SS

第二个SS

编辑:我添加prepareForReuse方法关于下面的答案,但现在细胞显示只是白色.难道我做错了什么?

    override func prepareForReuse() {
    super.prepareForReuse()

    teamLbl.text = ""
    myVoteLbl.text = ""
    profileImage.image = nil
}
Run Code Online (Sandbox Code Playgroud)

我还使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个单元格在内存中看起来是正确的. 记忆截图

jol*_*uly 2

我也有同样的问题_NSContiguousString。即使在只有一个标签的空应用程序中,它也会显示 48 字节的泄漏。

在此输入图像描述

我不确定,但我猜测泄漏的原因可能是与 NSString 桥接(我也在[_SwiftNativeNSStringBase retain]堆栈跟踪的顶部看到)。

在此输入图像描述

我发现 NSString 本身可能被Instruments视为泄漏对象,因为它是作为 Singleton 实现的。

我自己的问题在这里:Memory Leaks when allocateing text to UILabel (iOS, Swift 4, XCode9)