zoukankan      html  css  js  c++  java
  • 讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(上)

    前言:

    好久没写文章了,最近先是重构IT恋、又重写IT恋中。

    Sagit框架也不断的更新,调整,现在感觉已完美了了相当的多。

    今天不写教程,先简单分享一下技术内容。

    1:见Block必有:#define WeakSelf __weak typeof(self) this = self;

     故事要从这这里说起:

    当初番完这代码后,发现到处都有这个鬼东西,然后就去百度了一下,然后大意是为了:

    解决双向引用导致的内存无法释放问题

    简单的表述如下:

    self   强引用指向=》block;
    
    block 也强引用批向=>self;

    这时候就出事了,解决的方法是,把其中一个改成弱指向。

    而WeakSelf的定义,就是让block改成弱引用,这样无论self是不是强引用的指向block都无关紧要了。
    
    当然,更精致的做法是:先预判self有没有强引用指向block,没有,就不用WeakSelf定义了。
    
    不过,一般新手搞不明白内涵,无法做出有效的预判,所以见block就有WeakSelf也就相随相生了。

    2:其它场景的双向引用:UIViewController与UIView的纠缠

     首先,默认UIViewController有一个强引用指向了UIView,这是系统定义的,我们改不了:

    所以,如果UIView里再出现strong或retain指回UIController,就会导致UIViewController和UIView双双无法释放问题。

    这个问题,在我刚写Sagit框架时,只在意功能,没在意这些,就犯了这个错误:

    错误的写法是这样的:

    现在改正后的写法是这样的:

    3:事情没有这么简单:UIView子控件绑定事件指向Controller

    看一行Sagit的代码,关注后面的addClick:

    [[[[sagit addButton:@"Login" title:@"登录" font:40] 450 height:80] onBottom:@"pwdLine" y:149] addClick:@"loginClick"];

    对于事件流程关于Sagit的前面几篇有说了,这里说一下框架的流程代码:

    1:系统自动添加了一个UITapGestureRecognizer,并指定到一个固定的click方法;
    
    2:将方法名称和target存到自身的NSDictionary的字典中(框架为每个UIView都扩展了一个Dictionary)(就是这里造成强引用了Controller).
    
    3:事件点击时:先触发系统默认的click,然后click事件:从字典里取出方法名和target,找到SEL并动态执行。
    
    PS:设计成动态执行的好处:可以在执行前处理一些其它事情:比如将addClick参数:loginClick改成AgeButton.click,这样可以分解参数后,去执行AgeButton上的事件

    执行的代码是这样的,由于是动态执行,少不了还有一个警告:

    接下来,就是怎么消灭事件里对Controller的强引用:

    1:找了资料,发现有个NSMapTable,是弱引用的字典,于是把NSDictionary换成它,结果:参不忍睹,界面错乱。【大概是弱引用特别容易丢失数据】 
    2:尝试用一个全局的第三方的字典来存,结果也悲哀了! 3:最后想到了一个方法,不直接存Controller,只存字符串:1和0 ,在最终执行的时候,再去找。

    代码是这样的:

    真难为我这么聪明,想着大功告成,运行,释放了,成功了!!!

    然后又悲哀了:

    然后就动不动就到main含数了,让我怎么猜?说好的全局断点呢?你咋不断呢?

    搜了搜百度,想想要调度内存,那就一个蛋腾,还是靠猜吧。

    后来,根据释放的顺序,和最后的关键字,大概是这样猜的:

    控制器被释放了,这时候UIView还没释放,然后系统又给UIView绑字的事件发消息,结果遇到野指针,悲伤的故事发生了。

    于是,我做了一个艰难的决定,在UIController的deallow中写了这样的代码:

    -(void)dealloc
    {
        [self.view removeAllsubViews];//处理内存释放后的异常。
        NSLog(@"%@ ->UIViewControlelr relase", [self class]);
    }

    这执行dealloc前,毕竟Controller还是活着的,这时候赶紧把UIView的东西给清了,然后,发现完美,运行起来很6!

    总结:

    当我很6的解决完上述问题后,就开始写文章了想分享一下了,然后写了开头,发现:

    咦,好像UITableView和UITableViewCell,好像也有双向引用问题。

    因为我给Cell加了个属性,指向Table,运行,果然,Shit,连Controller和父的UIView都释放了,你UITableView做为子UI居然不释放!!!!

    没天理,继续折腾,然后UITableView搞释放了,又发现UITableViewCell不释放了(这个Cell通常又会是一大堆UI)。

    再然后,发现Push两层回来,又挂Crash了。

    现在正在全力抢救!!!解决完再来写下篇!!!

    操,发现为了释放那点内存的代价,折腾起来真惨过不释放算了〜〜〜〜

  • 相关阅读:
    IDEA下Maven的pom文件导入依赖出现Auto build completed with errors
    org.apache.jasper.JasperException: java.lang.NullPointerException
    Eclipse下导入web项目(Some projects cannot be imported because they already exist in the workspace)
    JS中构造函数的方法定义在原型对象里
    JS变量赋值
    JDBC以及连接池连接MySQL出现时区错误问题
    chrome中如何截取整个网页
    java中final、super、static关键字的使用
    初识less
    根据当前时间显示问候语
  • 原文地址:https://www.cnblogs.com/cyq1162/p/8195220.html
Copyright © 2011-2022 走看看