您的位置:金沙手机版下载 > www.990.com > 双向引用导致的IOS内存泄漏

双向引用导致的IOS内存泄漏

2019-10-16 05:10

前言:

在管理完框架内存泄漏的难点后,见上篇:陈述Sagit.Framework消除:双向引用导致的IOS内部存储器泄漏(中)- IOS不敢问津的Bug

意识职业代码有三个地点的内部存款和储蓄器没释放,原因很也大致:

www.990.com 1

在block里用到了self,变成双向引用,然后就最早考虑怎么管理这么些标题。

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

只是框架这里非常,有多个特好用的文山会海,STLastXXX类别,是用宏定义的,并且该宏指向了self。

诸有此类好用的STLastXXXX宏定义类别,作者希望它能够不受限制的随处使用。

于是开首折腾之路:

煎熬一:在代码中重新定义宏?

上边包车型大巴代码,说白了正是使用了self,我们看一下宏定义:

www.990.com 2

万般的做法,际遇block,都陪伴有WeakSelf这东东,像这么:

www.990.com 3

www.990.com,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那一个宏会怎么着? 

比如那样定义:

www.990.com 4

然后这么使用:

www.990.com 5

总的看是自作者想多,编绎都卡住。

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

诸如那样定义:

#define sagit [Sagit share].Layout

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

www.990.com 6

然后,就是在种种基类(STController或S电视机iew)初步化时,将笔者的self.baseView赋值给它。

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

比如:

www.990.com 7

看起来某些效用,可是,要用这种方法,还得想想的更周详:

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

2:STView可以无限嵌套STView。

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

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

好呢,写这样多,便是说这些艺术是足以的,但无法不思量的更密切非常多些!!!!

同期以此点子,只是逃避了self,self依然不允许被存在。

就此,那一个情势,先暂放,看看有未有方法,打破self的循环引用先。

煎熬三:思索如何打破block和self的双向援用?

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

既然block中要允许self中存,就意味着block对self必然是强援用,辣么就只可以思量,假使让self对block只可以是弱援引了,只怕尚未援用!

先来看框架的一段代码:

www.990.com 8

被圈起来的代码,完结的下列图中圈起来的效益:

www.990.com 9

对此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无关的地方去了?

用三个第三方持有block,只要那一个第三方不和和self发生径直关乎,那么相应就不会有标题。

援用关系就改为:

第三方:强引用=》block

block:强引用=》self

此间的假释关系,第三方活着,就不会释放block,block活着,就不会自由self。

为此结论:依旧不自由。

也等于说,一顿代码写下来,就算免去了关联,但依旧没释放。

一旦第三方对block是弱引用呢?

为了那个试验,作者新建了叁个种类,然后在此个种类上,试了总体24小时:

试验进程并未有赢得的想要的结果,但精晓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可能被移除时带来的闪退问题。

末尾:作弄一个IOS的另三个坑,又是秘密的dealloc方法:

上一篇小说,讲到,要是对UIView扩大了dealloc那形式,引发的杀人案是:

导航栏:叁次后退就闪退。

这一篇,又被本身意识,假若对UIViewController增添dealloc这些办法,引发的血案:

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

给大伙上叁个图,先把dealloc张开:

www.990.com 10

接下来运维的功能:

www.990.com 11

坑吧,幸而有上叁遍的阅历,赶紧新建了贰个等级次序,然后用代码排除法,最后照旧排到dealloc这里来。

见状那作品的同学,你们又有什么不可去忽悠同事了。

总结:

一体化折腾完内部存款和储蓄器释放难题后,Sagit框架也快速了大多,或者是错觉。

IT连的创办实业的也在延续,招待大家持续关切,多谢!

本文由金沙手机版下载发布于www.990.com,转载请注明出处:双向引用导致的IOS内存泄漏

关键词: