V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
abellee
V2EX  ›  iOS

关于内存泄漏问题

  •  
  •   abellee · 2017-03-16 20:35:52 +08:00 · 2957 次点击
    这是一个创建于 2592 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如有一个 class A ,

    是一个 model ,

    我在首页加载一批 A 的数据,

    当点击某个按钮, push 了一个 view 进来,把其中一个 A 放在 view 的 init 里传进去,

    在 view 里暂存了 A,

    当点返回, view 的 deinit 被调用,但同时 A 内存泄漏。

    我是用了 PleakSniffer ,它显示的说可能内存泄漏,

    但本身 A 就是不需要被释放的,因为在首页还需要用。

    但如果我在 view 里,对引用 A 的变量设置 weak ,就不会报这个错,

    如果真的是因为这样造成泄漏,那我觉得如果我往一个 tableView 的 cell 里传我自定义的数据类,

    不全得泄漏吗?

    难道我都得设置成 weak ?

    第 1 条附言  ·  2017-03-17 15:18:49 +08:00

    我写一下示例代码吧,实际我发现加了weak也没什么用。

    // 数据类
    class A{
         var name : String!;
         var age: Int!;
    }
    
    // 主界面,是一个UINavigationController根视图
    class Main: UIViewController{
         var data: [A]              = [];
         func loadData(){
               // get data A from some web service
         }
    
         // call from some UIButton
         func buttonPressed(_ sender: UIButton){
                 let index = sender.tag;
                 let a = self.data[index];
                 let view = NewViewController(withData: a);
                 self.navigationController?.pushViewController(view, animated: true);
         }
    }
    
    // 新界面
    class NewViewController: UIViewController {
           let tableView : UITableView!;
           let data: A!;
    
           // 我现在改成下面这样了,也没用
           // weak var data: A!
    
           init(withData data: A){
                  self.data = data;
           }
           
           deinit{
                 print("\(type(of: self)) deinit");
           }
    }
    

    情况就是上面这样的,实际NewViewController里的deinit在返回后是被调用了的,但同时PLeakSniffer报了个Detect Possible Leak: ProjectName.A

    然后我在另外一个界面看了一下,情况类似上面的,但那个界面是二级界面,是一个UITableView 但这个界面里的数据,是翻页的,所以没有外部传入的数据,全是自己从服务器加载的,但点击后还是会进入一个详情界面,也就是三级界面,当退回到这个二级界面后,也报了这样的错误,但如果再退回到主界面,所有包含的对象的deinit 都会被调用。 由于开始写的时候 deinit里输出的方式是直接写字符串,后来把基类改成了print("(type(of: self)) deinit"); 子类的没删,都连着输出了两次,可见deinit是按正常顺序执行了的。

    所以我也怀疑像各位说的,我的理解就是这些检测工具,应该是类似通过监测deinit的方式,当一个类发生了deinit, 然后就把这个类里的自定义属性枚举一遍,尝试类似canResponseTo之类的操作,如果还是可以可以,就会报Leak

    13 条回复    2017-03-17 15:23:14 +08:00
    zhongdong
        1
    zhongdong  
       2017-03-16 22:45:47 +08:00   ❤️ 1
    没太明白楼主表达的意思。要查看 view 有没有释放,要看 dealloc 方法有没有调用
    abellee
        2
    abellee  
    OP
       2017-03-16 23:12:36 +08:00
    @zhongdong 不好意思 我想说的意思其实就是传值,从一个长驻界面,传一下自定义的数据类到新的视图里,新的视图里会暂存一下。
    新视图的 deinit 已经被调用了的,我的理解也就是已经释放了。
    但传进来的数据类报了可能内存泄漏
    abellee
        3
    abellee  
    OP
       2017-03-16 23:18:03 +08:00
    @zhongdong
    push 自定义数据类
    长驻界面 -----------------------------------------------> 新界面
    pop 新界面的 deinit 被调用,即释放
    长驻界面 <----------------------------------------------- 新界面

    就这么个过程,但在新界面释放后, PleakSniffer 报了个自定义数据类 可能内存泄漏的提示
    acumen
        4
    acumen  
       2017-03-17 00:14:44 +08:00 via iPhone
    从引用计数来看,常驻页面持有 A 对象, A 的引用计数 +1 ,此时 A 对象被传到新页面,新页面也持有 A 对象 A 的引用计数再 +1 ,当新页面被 deinit 的时候 A 引用计数 -1 ,最后 A 的引用计数不等于 0 也就是说 常驻页面还持有 A 对象(不知道是不是 lz 表达的意思
    paradoxs
        5
    paradoxs  
       2017-03-17 00:20:40 +08:00 via iPhone
    来个 demo 谢谢
    acumen
        6
    acumen  
       2017-03-17 00:22:13 +08:00 via iPhone
    @acumen 内存泄漏通俗地讲是 该释放的没有释放, A 对象不该在新页面 deinit 被释放啊,不算内存泄漏吧
    dangyuluo
        7
    dangyuluo  
       2017-03-17 01:46:35 +08:00
    内存泄漏往往是密封垫圈老化,更换密封垫圈即可。
    zhongdong
        8
    zhongdong  
       2017-03-17 07:53:37 +08:00 via Android
    感觉不是这个 model 造成的内部泄露。要不上个 Demo ?
    XDDD
        9
    XDDD  
       2017-03-17 08:09:08 +08:00 via iPhone
    报的是“可能的”内存泄漏,不是已经泄漏了。你的 vc 释放了,但是持有的 model 还在,那不是很可疑吗。在你的例子里,这不是内存泄漏。但是程序并没有这么聪明,查不出来
    expkzb
        10
    expkzb  
       2017-03-17 09:46:15 +08:00
    @XDDD 说的对
    @abellee 这种情况你就应该使用 weak
    eato
        11
    eato  
       2017-03-17 10:34:24 +08:00 via iPhone
    来段出问题的代码看看?
    XDDD
        12
    XDDD  
       2017-03-17 12:51:44 +08:00 via iPhone
    @expkzb 不应该用 weak 。
    lz 的描述是“放在 init 里传进去”。这个 view 是用来展示这个 model 的,当然应该持有它。
    换个场景,如果传进来的是一个 autorelease 的临时对象,那不是一传进来就没了?

    @abellee 什么都不用做,忽略这个警告就好了。或许你的分析工具能够通过手动添加白名单来消除警告
    abellee
        13
    abellee  
    OP
       2017-03-17 15:23:14 +08:00
    @acumen
    @paradoxs
    @dangyuluo
    @zhongdong
    @XDDD
    @expkzb
    @eato

    上示例代码了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2508 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 15:51 · PVG 23:51 · LAX 08:51 · JFK 11:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.