双向引用导致的IOS内部存款和储蓄器泄漏(中)-,双向引用导致的IOS内部存款和储蓄器泄漏(中)-

前言:

在处理完框架内部存款和储蓄器泄漏的题材后,见上篇:叙述Sagit.Framework化解:双向引用导致的IOS内部存款和储蓄器泄漏(中)-
IOS鲜为人知的Bug

发现工作代码有二个地点的内部存款和储蓄器没释放,原因很也大约:

图片 1

在block里用到了self,造成双向引用,然后就开首思索怎么处理这一个标题。

平日则合计,正是改代码,block不要用到self,或只用self的弱引用。

只是框架那里越发,有四个特好用的层见迭出,STLastXXX种类,是用宏定义的,而且该宏指向了self。

如此那般好用的STLastXXXX宏定义体系,小编梦想它可以不受限制的所在使用。

于是伊始折腾之路:

前言:

在拍卖完框架内部存储器泄漏的题材后,见上篇:讲述Sagit.Framework化解:双向引用导致的IOS内部存款和储蓄器泄漏(中)-
IOS不敢问津的Bug

意识事情代码有多个位置的内部存储器没释放,原因很也简要:

图片 2

在block里用到了self,造成双向引用,然后就从头思索怎么处理这些题材。

例行则合计,就是改代码,block不要用到self,或只用self的弱引用。

只是框架那Ritter别,有二个特好用的俯十地芥,STLastXXX体系,是用宏定义的,而且该宏指向了self。

如此好用的STLastXXXX宏定义类别,作者期望它可以不受限制的三街陆巷使用。

于是乎从头折腾之路:

煎熬1:在代码中重复定义宏?

地点的代码,说白了正是利用了self,我们看一下宏定义:

图片 3

日常的做法,蒙受block,都陪伴有WeakSelf那东东,像那样:

图片 4

STWeakSelf的暗中认可定义:

//block块中用的引用
#define STWeakSelf __weak typeof(self) selfWeak = self;__weak typeof(selfWeak) this = selfWeak;
#define STWeakObj(o) __weak typeof(o) o##Weak = o;
#define STStrongObj(o) __strong typeof(o) o = o##Weak;

差不多,宏定义正是编绎期的文字替换游戏,假诺本身在block里的第二行代码,重新定义sagit那么些宏会怎么着? 

譬如说那样定义:

图片 5

接下来这么使用:

图片 6

总的看是自家想多,编绎都短路。

煎熬一:在代码中再次定义宏?

上边的代码,说白了正是运用了self,大家看一下宏定义:

图片 7

普普通通的做法,际遇block,都陪伴有WeakSelf那东东,像这样:

图片 8

STWeakSelf的暗中认可定义:

//block块中用的引用
#define STWeakSelf __weak typeof(self) selfWeak = self;__weak typeof(selfWeak) this = selfWeak;
#define STWeakObj(o) __weak typeof(o) o##Weak = o;
#define STStrongObj(o) __strong typeof(o) o = o##Weak;

简单易行,宏定义正是编绎期的文字替换游戏,即便本人在block里的首先行代码,重新定义sagit这些宏会怎么着? 

诸如那样定义:

图片 9

接下来这么使用:

图片 10

看来是笔者想多,编绎都不通。

煎熬2:将宏定义指向三个函数

譬如说那样定义:

#define sagit [Sagit share].Layout

接下来跑到Sagit那几个类里定义二个UIView* Layout属性,比如那样:

图片 11

下一场,就是在挨家挨户基类(STController或STView)开头化时,将本身的self.baseView赋值给它。

PS:baseView是对UIView和UIViewController扩张的1个属性,都指向View。

比如:

图片 12

看起来某些成效,可是,要用这种艺术,还得想想的更周到:

1:架框中每个STView都是baseView。

2:STView可以无限嵌套STView。

3:因此:在STView被初时化时,设置它为baseView,加载完后,如果STView有父的STView,交还控制权,(这里就涉及到嵌套的控制流程,如果各个子View是异步加载,那就悲催了)。

4:没有继承自STView的情况呢,怎么拦截View的开始和结束呢?(Sagit已经慢慢弱化基类的功能,多数功能都是在原生的上做扩展,所以不用STView,很多功能也可以正常使用)

好呢,写这么多,正是说那几个艺术是能够的,但必须思量的更密切好多些!!!!

而且那个办法,只是逃避了self,self依旧区别意被存在。

于是,这几个措施,先暂放,看看有没有措施,打破self的巡回引用先。

煎熬二:将宏定义指向3个函数

比如那样定义:

#define sagit [Sagit share].Layout

下一场跑到Sagit那个类里定义二个UIView* Layout属性,比如那样:

图片 13

然后,正是在壹壹基类(STController或STView)初步化时,将自笔者的self.baseView赋值给它。

PS:baseView是对UIView和UIViewController扩张的二个属性,都指向View。

比如:

图片 14

看起来有个别功效,然而,要用那种办法,还得商讨的更完美:

1:架框中每个STView都是baseView。

2:STView可以无限嵌套STView。

3:因此:在STView被初时化时,设置它为baseView,加载完后,如果STView有父的STView,交还控制权,(这里就涉及到嵌套的控制流程,如果各个子View是异步加载,那就悲催了)。

4:没有继承自STView的情况呢,怎么拦截View的开始和结束呢?(Sagit已经慢慢弱化基类的功能,多数功能都是在原生的上做扩展,所以不用STView,很多功能也可以正常使用)

好呢,写那样多,正是说那个艺术是可以的,但不可能不挂念的更密切好多些!!!!

同时那个办法,只是逃避了self,self如故不容许被存在。

于是,这一个情势,先暂放,看看有未有点子,打破self的循环引用先。

折腾3:考虑什么打破block和self的双向引用?

 block和self,互相的强引用,要打破它,总得有一方要示弱。

既然block中要允许self中存,就代表block对self必然是强引用,辣么就不得不思量,借使让self对block只可以是弱引用了,只怕未有引用!

先来看框架的1段代码:

图片 15

被圈起来的代码,达成的下列图中圈起来的功能:

图片 16

对于Sagit中,达成表格的代码是否觉的很不难,但是教程还没写,那里提前败露了。

抑或说根本,重点为UITableView中,扩充了一个属性AddCell的Block属性,见代码如下:

@interface UITableView(ST)

#pragma mark 核心扩展
typedef void(^AddTableCell)(UITableViewCell *cell,NSIndexPath *indexPath);
typedef BOOL(^DelTableCell)(UITableViewCell *cell,NSIndexPath *indexPath);
typedef void(^AfterTableReloadData)(UITableView *tableView);
//!用于为Table追加每一行的Cell
@property (nonatomic,copy) AddTableCell addCell;
//!用于为Table移除行的Cell
@property (nonatomic,copy) DelTableCell delCell;
//!用于为Table reloadData 加载完数据后触发
@property (nonatomic,copy) AfterTableReloadData afterReload;
//!获取Table的数据源
@property (nonatomic,strong) NSMutableArray<id> *source;
//!设置Table的数据源
-(UITableView*)source:(NSMutableArray<id> *)dataSource;

虽说定义了二个addCell属性,但是怎么拥有,还得看getter和setter怎么落到实处:

@implementation UITableView(ST)

#pragma mark 核心扩展

-(AddTableCell)addCell
{
    //从第三方持有返回
}
-(void)setAddCell:(AddTableCell)addCell
{
    if(addCell!=nil)
    {
         //第三方持有addCell
    }
    else
    {

    }
}

假使那里,把存档addCell这一个block存到和UITableView无关的地方去了?

用一个第2方持有block,只要那些第1方不和和self产生直接关系,那么相应就不会极度。

引用关系就改成:

第三方:强引用=》block

block:强引用=》self

此处的放出关系,第二方活着,就不会释放block,block活着,就不会自由self。

由此结论:还是不自由。

也正是说,壹顿代码写下来,即便免去了关乎,但照旧没释放。

折腾三:思虑什么打破block和self的双向引用?

 block和self,相互的强引用,要打破它,总得有壹方要示弱。

既然block中要允许self中存,就代表block对self必然是强引用,辣么就不得不考虑,如若让self对block只可以是弱引用了,或许未有引用!

先来看框架的1段代码:

图片 17

被圈起来的代码,完毕的下列图中圈起来的意义:

图片 18

对于Sagit中,落成表格的代码是或不是觉的很简短,可是教程还没写,这里提前走漏了。

也许说根本,重点为UITableView中,扩大了1个属性AddCell的Block属性,见代码如下:

@interface UITableView(ST)

#pragma mark 核心扩展
typedef void(^AddTableCell)(UITableViewCell *cell,NSIndexPath *indexPath);
typedef BOOL(^DelTableCell)(UITableViewCell *cell,NSIndexPath *indexPath);
typedef void(^AfterTableReloadData)(UITableView *tableView);
//!用于为Table追加每一行的Cell
@property (nonatomic,copy) AddTableCell addCell;
//!用于为Table移除行的Cell
@property (nonatomic,copy) DelTableCell delCell;
//!用于为Table reloadData 加载完数据后触发
@property (nonatomic,copy) AfterTableReloadData afterReload;
//!获取Table的数据源
@property (nonatomic,strong) NSMutableArray<id> *source;
//!设置Table的数据源
-(UITableView*)source:(NSMutableArray<id> *)dataSource;

虽说定义了三个addCell属性,可是怎么拥有,还得看getter和setter怎么落实:

@implementation UITableView(ST)

#pragma mark 核心扩展

-(AddTableCell)addCell
{
    //从第三方持有返回
}
-(void)setAddCell:(AddTableCell)addCell
{
    if(addCell!=nil)
    {
         //第三方持有addCell
    }
    else
    {

    }
}

假定那里,把存档addCell这些block存到和UITableView毫无干系的地点去了?

用贰个第一方持有block,只要那几个第3方不和和self发生直接关系,那么相应就不会格外。

引用关系就改成:

第三方:强引用=》block

block:强引用=》self

那边的释放关系,第1方活着,就不会自由block,block活着,就不会放出self。

就此结论:依然不自由。

也正是说,1顿代码写下来,即使免去了关联,但依旧没释放。

比方第一方对block是弱引用呢?

为了那么些实验,作者新建了一个品种,然后在这么些项目上,试了全副二四钟头:

实验进度并未获取的想要的结果,但理解block的原理,和判断自个儿的逻辑误区。

试行的拿走的文化前面再享受,那里先接二连三:

由于这里addCell,必须始终存活,因为你不知道什么时候,可能又要UITableView的reloadData一下。

所以,addCell这个block,必须被强引用,而且生命周期得和UITableView一致。

所以,要打破这个核心,还是得有第三方行为事件来触发破除关系,故事就转变成为:由self去移除第三方。

如果一定义要由某个事件来触发解除关系,那么第三方也没存在的必要了。

因为正常A和B互相引用无解,是指他们互相无解,但只要有第三者存在,对其中一个置nil就解了。

末尾的结尾,框架的代码是那般的:

-(AddTableCell)addCell
{
    return [self key:@"addCell"];
}
-(void)setAddCell:(AddTableCell)addCell
{
    if(addCell!=nil)
    {
        addCell=[addCell copy];
        [self key:@"addCell" value:addCell];
    }
    else
    {
        [self.keyValue remove:@"addCell"];
    }
}

依然是用强引用来存档block,那里有2个注意事项:

若果第2方对block是弱引用呢?

为了那一个实验,小编新建了四个类型,然后在这些体系上,试了全方位二四小时:

尝试进程并未有取得的想要的结果,但掌握block的原理,和判断自身的逻辑误区。

试验的获得的学问后边再享受,那里先几次三番:

由于这里addCell,必须始终存活,因为你不知道什么时候,可能又要UITableView的reloadData一下。

所以,addCell这个block,必须被强引用,而且生命周期得和UITableView一致。

所以,要打破这个核心,还是得有第三方行为事件来触发破除关系,故事就转变成为:由self去移除第三方。

如果一定义要由某个事件来触发解除关系,那么第三方也没存在的必要了。

因为正常A和B互相引用无解,是指他们互相无解,但只要有第三者存在,对其中一个置nil就解了。

末尾的末段,框架的代码是那样的:

-(AddTableCell)addCell
{
    return [self key:@"addCell"];
}
-(void)setAddCell:(AddTableCell)addCell
{
    if(addCell!=nil)
    {
        addCell=[addCell copy];
        [self key:@"addCell" value:addCell];
    }
    else
    {
        [self.keyValue remove:@"addCell"];
    }
}

依旧是用强引用来存档block,那里有五个注意事项:

设若您想将三个block持久化,先copy一下,不然你会死的十分惨。

好呢,让它们相互强引用吧,剩下的事,正是哪个人来当以此第三方,以及怎么消除那层关系!!!

于是,到导航栏后退事件中,拦截,并做销毁工作:

@implementation UINavigationController (ST)

#pragma mark NavigationBar 的协议,这里触发
// fuck shouldPopItem 方法存在时,只会触发导航栏后退,界面视图却不后退。
//- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item  // same as push methods
//{
////    //重设上一个Controller的导航(不然在二次Push后再Pop会Crash)
////    NSInteger count=self.viewControllers.count;
////    if(count>0)//发现这里返回的viewControllers,已经是移掉了当前的Controller后剩下的。
////    {
////        UIViewController *preController=self.viewControllers[count-1];//获取上一个控制器
////        if([preController needNavBar])
////        {
////            [preController reSetNav:self];
////        }
////    }
////
//    return YES;
//}
//返回到当前页面
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
{
//    if(navigationBar!=nil && [navigationBar.lastSubView isKindOfClass:[UIButton class]])
//    {
//       // [navigationBar.lastSubView height:0];//取消自定义复盖的UIButton
//    }
    NSInteger count=self.viewControllers.count;
    if(count>0)
    {
        UIViewController *current=self.viewControllers[count-1];
        self.navigationBar.hidden=![current needNavBar];
        if(self.tabBarController!=nil)
        {
            self.tabBarController.tabBar.hidden=![current needTabBar];
        }
        //检测上一个控制器有没有释放
        UIViewController *nextController=current.nextController;
        if(nextController!=nil)
        {
            [nextController dispose];
            nextController=nil;
        }
    }
}
-(void)dealloc
{
    NSLog(@"UINavigationController relase -> %@", [self class]);
}

Sagit框架为:每一个view和controller扩张了dispose方法,里面清掉键值对,等于把block置为nil,解除了关联。

除开导航后退,还索要拦截多二个轩然大波,就是presentViewController的轩然大波跳转时,也必要检查测试并销毁。

搞活那两步之后,将来就足以轻松的在block里写self了,爱引用就引述了,反正传说的末段,都有多少个外人来终止

而且强引用有八个便宜:

1:再也用不上WeakSelf这种定义了。

2:由于是强引用,就不用去管:里面还要套个StrongSelf,去避开多线程时,self可能被移除时带来的闪退问题。

假使你想将二个block持久化,先copy一下,不然你会死的非常惨。

行吗,让它们互相强引用吧,剩下的事,正是何人来当这一个第2方,以及怎么化解那层关系!!!

于是乎,到导航栏后退事件中,拦截,并做销毁工作:

@implementation UINavigationController (ST)

#pragma mark NavigationBar 的协议,这里触发
// fuck shouldPopItem 方法存在时,只会触发导航栏后退,界面视图却不后退。
//- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item  // same as push methods
//{
////    //重设上一个Controller的导航(不然在二次Push后再Pop会Crash)
////    NSInteger count=self.viewControllers.count;
////    if(count>0)//发现这里返回的viewControllers,已经是移掉了当前的Controller后剩下的。
////    {
////        UIViewController *preController=self.viewControllers[count-1];//获取上一个控制器
////        if([preController needNavBar])
////        {
////            [preController reSetNav:self];
////        }
////    }
////
//    return YES;
//}
//返回到当前页面
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
{
//    if(navigationBar!=nil && [navigationBar.lastSubView isKindOfClass:[UIButton class]])
//    {
//       // [navigationBar.lastSubView height:0];//取消自定义复盖的UIButton
//    }
    NSInteger count=self.viewControllers.count;
    if(count>0)
    {
        UIViewController *current=self.viewControllers[count-1];
        self.navigationBar.hidden=![current needNavBar];
        if(self.tabBarController!=nil)
        {
            self.tabBarController.tabBar.hidden=![current needTabBar];
        }
        //检测上一个控制器有没有释放
        UIViewController *nextController=current.nextController;
        if(nextController!=nil)
        {
            [nextController dispose];
            nextController=nil;
        }
    }
}
-(void)dealloc
{
    NSLog(@"UINavigationController relase -> %@", [self class]);
}

Sagit框架为:每一个view和controller扩充了dispose方法,里面清掉键值对,等于把block置为nil,解除了关乎。

而外导航后退,还必要拦截多3个风云,就是presentViewController的风云跳转时,也亟需检查实验并销毁。

盘活那两步之后,今后就能够轻松的在block里写self了,爱引用就引述了,反正好玩的事的终极,都有三个面生人来了却

并且强引用有3个功利:

1:再也用不上WeakSelf这种定义了。

2:由于是强引用,就不用去管:里面还要套个StrongSelf,去避开多线程时,self可能被移除时带来的闪退问题。

提及底:作弄二个IOS的另多少个坑,又是地下的dealloc方法:

上一篇文章,讲到,如若对UIView增加了dealloc这格局,引发的血案是:

导航栏:三遍后退就闪退。

那壹篇,又被本身意识,即便对UIViewController扩张dealloc那几个措施,引发的凶杀案:

UIAlertView:当alertViewStyle设置为带文本框时就闪退。

给大伙儿上1个图,先把dealloc打开:

图片 19

下一场运维的意义:

图片 20

坑吧,万幸有上一遍的经验,赶紧新建了1个体系,然后用代码排除法,最后依旧排到dealloc那里来。

见到那作品的校友,你们又能够去忽悠同事了。

聊到底:戏弄八个IOS的另二个坑,又是秘密的dealloc方法:

上一篇小说,讲到,要是对UIView扩张了dealloc那措施,引发的谋杀案是:

导航栏:二遍后退就闪退。

这壹篇,又被笔者发觉,借使对UIViewController扩张dealloc这一个办法,引发的血案:

UIAlertView:当alertViewStyle设置为带文本框时就闪退。

给大家上一个图,先把dealloc打开:

图片 21

然后运转的效果:

图片 22

坑吧,万幸有上3遍的阅历,赶紧新建了3个项目,然后用代码排除法,最后仍旧排到dealloc那里来。

看样子那文章的校友,你们又足以去忽悠同事了。

总结:

总体折腾完内部存款和储蓄器释放问题后,Sagit框架也快速了广大,只怕是错觉。

IT连的创业的也在继承,欢迎大家持续关怀,谢谢!

总结:

完整折腾完内部存款和储蓄器释放难题后,Sagit框架也急忙了诸多,只怕是错觉。

IT连的创业的也在继承,欢迎我们持续关心,多谢!

相关文章