前言:
在处理终结框架内存泄漏的题材后,见上篇:叙Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)-
IOS不为人知的Bug
意识事情代码来一个地方的内存没释放,原因十分啊简要:
每当block里之所以到了self,造成双向引用,然后就起来琢磨怎么处理此问题。
正常则合计,就是改代码,block不要就此到self,或只所以self的弱引用。
特是框架这里特别,有一个单独好用底文山会海,STLastXXX系列,是因此宏定义的,而且该宏指向了self。
这么好用的STLastXXXX宏定义系列,我欲它们好无被限制的所在使用。
于是乎从头折腾的路:
讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)- block中任性用self,sagit.frameworkios
折腾一:在代码中再度定义宏?
地方的代码,说白了就是下了self,我们看一下宏定义:
通常的做法,遇到block,都陪产生WeakSelf这东东,像这么:
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这个宏会怎样?
照这样定义:
下一场这么使用:
看来是自家思念多,编绎都过不去。
前言:
当拍卖完毕框架内存泄漏的问题后,见上篇:讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)-
IOS不为人知的Bug
发觉事情代码来一个地方的内存没释放,原因深为略:
在block里用到了self,造成双向引用,然后便起考虑怎么处理此题材。
好端端则合计,就是改代码,block不要用到self,或单独所以self的弱引用。
才是框架这里特别,有一个独好用的多重,STLastXXX系列,是用宏定义的,而且该宏指向了self。
诸如此类好用的STLastXXXX宏定义系列,我期待她可免受限制的各地使用。
乃开始折腾的路:
煎熬二:将宏定义指向一个函数
按照这样定义:
#define sagit [Sagit share].Layout
接下来跑至Sagit这个类里定义一个UIView* Layout属性,比如这样:
接下来,就是当相继基类(STController或STView)初始化时,将自之self.baseView赋值给它。
PS:baseView是对准UIView和UIViewController扩展的一个特性,都依赖为View。
比如:
看起有点成效,不过,要用这种方式,还得想的复周到:
1:架框中每个STView都是baseView。
2:STView可以无限嵌套STView。
3:因此:在STView被初时化时,设置它为baseView,加载完后,如果STView有父的STView,交还控制权,(这里就涉及到嵌套的控制流程,如果各个子View是异步加载,那就悲催了)。
4:没有继承自STView的情况呢,怎么拦截View的开始和结束呢?(Sagit已经慢慢弱化基类的功能,多数功能都是在原生的上做扩展,所以不用STView,很多功能也可以正常使用)
好吧,写这样多,就是说是方法是好的,但得考虑的重复密切好多来!!!!
并且这点子,只是逃避了self,self仍然未容许为有。
因而,这个措施,先暂时放,看看发生没有出法子,打破self的轮回引用先。
折腾一:在代码中又定义宏?
点的代码,说白了就运用了self,我们看一下宏定义:
常备的做法,遇到block,都陪伴产生WeakSelf这东东,像这么:
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这个宏会怎样?
仍这样定义:
下一场这么使用:
总的看是我想多,编绎都卡住。
折腾三:思考什么打破block和self的双向引用?
block与self,互相的高引用,要打破它,总得有同一着要示弱。
既然block中一旦允许self中存,就意味着block对self必然是高引用,辣么就不得不思考,如果为self对block只能是已故引用了,或者无引用!
优先来拘禁框架的同样截代码:
吃圈起来的代码,实现的下列图被围绕起来的功力:
对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属性,比如这样:
接下来,就是在依次基类(STController或STView)初始化时,将自家的self.baseView赋值给其。
PS:baseView是对准UIView和UIViewController扩展的一个特性,都负为View。
比如:
看起有点成效,不过,要就此这种方式,还得考虑的再次周全:
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只能是已故引用了,或者没有引用!
预先来拘禁框架的平等段子代码:
于缠起来的代码,实现之下列图中围绕起来的效益:
对于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了,容易引用就引述了,反正故事的末尾,都发出一个外人来终止。
同时愈澳门1495娱乐引用有一个利益:
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打开:
然后运行的功效:
坑吧,好以生高达亦然不好的涉,赶紧新建了一个列,然后据此代码排除法,最终还是解除到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打开:
下一场运行的功用:
坑吧,好于来高达亦然次等的更,赶紧新建了一个型,然后据此代码排除法,最终还是排除到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 前言:
在拍卖完框架内存泄漏的题目后,见上篇…