菜单

讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)- block中任性用self讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)- block中任性用self,sagit.frameworkios

2018年9月19日 - 中甲报道

前言:

于拍卖终结框架内存泄漏的题目后,见上篇:叙述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)-
IOS不为人知的Bug

察觉工作代码来一个地方的内存没释放,原因很啊略:

图片 1

以block里之所以到了self,造成双向引用,然后便起来想怎么处理此题材。

健康则合计,就是改代码,block不要就此到self,或独自所以self的弱引用。

仅是框架这里特别,有一个仅仅好用的泛滥成灾,STLastXXX系列,是用宏定义的,而且该宏指向了self。

这么好用的STLastXXXX宏定义系列,我望它们可以不深受限制的所在使用。

于是乎从头折腾的路:

讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)- block中任性用self,sagit.frameworkios

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

方的代码,说白了就使了self,我们看一下宏定义:

图片 2

一般说来的做法,遇到block,都伴随有WeakSelf这东东,像这样:

图片 3

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这个宏会怎样? 

仍这样定义:

图片 4

下一场这么使用:

图片 5

总的看是自思多,编绎都不通。

前言:

当拍卖完框架内存泄漏的题目后,见上篇:讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)-
IOS不为人知的Bug

发觉事情代码有一个地方的内存没释放,原因非常啊大概:

图片 6

于block里之所以到了self,造成双向引用,然后就从头考虑怎么处理这个题材。

好端端则想,就是改代码,block不要因此到self,或仅仅所以self的弱引用。

止是框架这里特别,有一个止好用的一连串,STLastXXX系列,是为此宏定义的,而且该宏指向了self。

这样好用的STLastXXXX宏定义系列,我望它可以不给限制的各处使用。

遂开始折腾之路:

折腾二:将宏定义指向一个函数

依照这样定义:

#define sagit [Sagit share].Layout

接下来跑至Sagit这个类里定义一个UIView* Layout属性,比如这样:

图片 7

下一场,就是在相继基类(STController或STView)初始化时,将自身之self.baseView赋值给它们。

PS:baseView是指向UIView和UIViewController扩展的一个性,都凭借于View。

比如:

图片 8

关押起有些成效,不过,要为此这种艺术,还得想想的更完美:

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

2:STView可以无限嵌套STView。

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

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

吓吧,写这样多,就是说是方法是可的,但要考虑的重复细好多几!!!!

而且是法,只是逃避了self,self仍然不同意给在。

所以,这个法子,先小放,看看发生无出点子,打破self的巡回引用先。

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

点的代码,说白了即使用了self,我们看一下宏定义:

图片 9

寻常的做法,遇到block,都陪产生WeakSelf这东东,像这么:

图片 10

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这个宏会怎样? 

遵照这样定义:

图片 11

下一场这么使用:

图片 12

总的看是自眷恋多,编绎都打断。

折腾三:思考如何打破block和self的双向引用?

 block及self,互相的强引用,要打破它,总得有同等着要示弱。

既然block中一经允许self中存,就代表block对self必然是强引用,辣么就只好思考,如果吃self对block只能是弱引用了,或者无引用!

先来拘禁框架的均等段代码:

图片 13

叫缠起来的代码,实现的下列图被围绕起来的成效:

图片 14

对此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。

故结论:还是勿放。

也就是说,一搁浅代码写下去,虽然破除了关联,但要没有释放。

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

遵循这样定义:

#define sagit [Sagit share].Layout

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

图片 15

接下来,就是当一一基类(STController或STView)初始化时,将本身的self.baseView赋值给它。

PS:baseView是针对UIView和UIViewController扩展的一个性质,都靠为View。

比如:

图片 16

在押起有点成效,不过,要就此这种方式,还得琢磨的复周全:

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

2:STView可以无限嵌套STView。

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

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

吓吧,写这么多,就是说这措施是足以的,但不能不考虑的再周密好多把!!!!

同时这个办法,只是逃避了self,self仍然不容许让在。

故,这个措施,先暂时放,看看发生无发出道,打破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和self的双向引用?

 block及self,互相的赛引用,要打破其,总得有一致方要示弱。

既然block中要允许self中存,就象征block对self必然是青出于蓝引用,辣么就只好思考,如果叫self对block只能是去世引用了,或者尚未引用!

事先来拘禁框架的同段落代码:

图片 17

吃缠起来的代码,实现之下列图中围绕起来的效能:

图片 18

对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持久化,先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是物化引用呢?

为是实验,我新建了一个档,然后于斯路达,试了整整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,这里发生一个注意事项:

末:吐槽一个IOS的别一个坑,又是地下的dealloc方法:

落得平等首稿子,讲到,如果对UIView扩展了dealloc这道,引发的谋杀案是:

导航栏:二浅后降落就闪退。

及时同样首,又于自己意识,如果对UIViewController扩展dealloc这个艺术,引发的凶杀案:

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

让大伙上一个图,先将dealloc打开:

图片 19

接下来运行的功用:

图片 20

坑吧,好当有上同一坏的阅历,赶紧新建了一个型,然后据此代码排除法,最终还是排除到dealloc这里来。

望就文章的同班,你们还要可去忽悠同事了。

比方你想用一个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可能被移除时带来的闪退问题。

总结:

完折腾了内存释放问题后,Sagit框架为快了累累,也许是错觉。

IT连的创业之呢当延续,欢迎大家持续关注,谢谢!

最终:吐槽一个IOS的另外一个坑,又是潜在之dealloc方法:

高达同篇稿子,讲到,如果对UIView扩展了dealloc这方式,引发的凶杀案是:

导航栏:二不良后回落就闪退。

当即无异于首,又为自己发现,如果对UIViewController扩展dealloc这个方式,引发的谋杀案:

UIAlertView:当alertViewStyle设置也牵动文本框时即便闪退。

吃大伙儿上一个贪图,先把dealloc打开:

图片 21

下一场运行的机能:

图片 22

坑吧,好于发高达等同赖的经历,赶紧新建了一个品种,然后用代码排除法,最终还是免到dealloc这里来。

望就文章的同班,你们还要好去忽悠同事了。

总结:

完全折腾了内存释放问题后,Sagit框架为火速了众多,也许是错觉。

IT连的创业之也罢在后续,欢迎大家持续关注,谢谢!

http://www.bkjia.com/IOSjc/1270679.htmlwww.bkjia.comtruehttp://www.bkjia.com/IOSjc/1270679.htmlTechArticle讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)-
block中任性用self,sagit.frameworkios 前言:
在拍卖完毕框架内存泄漏的题目后,见上篇…

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图