IOS不敢问津的Bug

前言:

话说今儿晚上要么今晚,写了一篇:叙述Sagit.Framework消除:双向引用导致的IOS内部存款和储蓄器泄漏(上)

文章写到最终时,多了无数莫名美妙的主题材料!!!

为了化解了那些莫名美妙的主题素材,作者又战役了二四小时〜〜〜

接下来终于化解了难题,原来是IOS的隐藏性Bug,只想恨恨的说一声fuck~~~

前言:

话说明晚要么今晚,写了壹篇:讲述Sagit.Framework化解:双向引用导致的IOS内部存款和储蓄器泄漏(上)

小说写到最终时,多了数不完莫名奇妙的难点!!!

为了缓慢解决了那个莫名美妙的主题素材,小编又战役了二肆钟头〜〜〜

下一场终于化解了难题,原来是IOS的隐藏性Bug,只想恨恨的说一声fuck~~~

传说出自:

逸事是如此的,为了管理内存释放的标题,普普通通的人的讨论,都以给目的的dealloc扩展日志输出。

于是乎,UIView、UIViewController和多少个Sagit定义的基类STView、STController,放任自流就拉长了这么一行代码:

-(void)dealloc{
    NSLog(@"UIView relase -> %@", [self class]);
}

然后就经过输出的日志,观望该措施被实施与否,来认可对象是还是不是健康被假释。

在各类强引用、弱引用、循环引用、先强后弱引用的坑里跳来跳去后,终于祭出了宏观的杀招,来管理这个难点:

-(id)key:(NSString *)key
{
    id value=[self.keyValue get:key];
    if(value==nil)
    {
        value=[self.keyValueWeak get:key];
    }
    return value;
}
-(UIView*)key:(NSString *)key valueWeak:(id)value
{
    [self.keyValueWeak set:key value:value];
    return self;
}
-(UIView*)key:(NSString *)key value:(id)value
{
    [self.keyValue set:key value:value];
    return self;
}
-(NSMapTable*)keyValueWeak
{
    NSMapTable *kv=[self.keyValue get:@"keyValueWeak"];
    if(kv==nil)
    {
        kv=[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableWeakMemory];
        [self.keyValue set:@"keyValueWeak" value:kv];
    }
    return kv;
//    NSMapTable *kv= (NSMapTable*)objc_getAssociatedObject(self, &keyValueWeakChar);
//    if(kv==nil)
//    {
//        kv=[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableWeakMemory];
//        objc_setAssociatedObject(self, &keyValueWeakChar, kv,OBJC_ASSOCIATION_RETAIN);
//    }
//    return kv;
}

-(NSMutableDictionary<NSString*,id>*)keyValue
{

    NSMutableDictionary<NSString*,id> *kv= (NSMutableDictionary<NSString*,id>*)objc_getAssociatedObject(self, &keyValueChar);
    if(kv==nil)
    {
        kv=[NSMutableDictionary<NSString*,id> new];
        objc_setAssociatedObject(self, &keyValueChar, kv,OBJC_ASSOCIATION_RETAIN);
    }
    return kv;
}

经过在强引用的Dictionary里,保存3个弱引用NSTableMap,来存档一些索要弱引用的目的,比方View或Controller等对象。

正当本身在思考上面包车型地铁解法的时候:另三个极度的导航栏Crash难题也应运而生了,而且在以下三种情况都会Crash掉:

传说出自:

遗闻是如此的,为了管理内部存款和储蓄器释放的难点,正常人的切磋,都以给目标的dealloc扩大日志输出。

于是乎,UIView、UIViewController和三个Sagit定义的基类STView、STController,放任自流就拉长了如此一行代码:

-(void)dealloc{
    NSLog(@"UIView relase -> %@", [self class]);
}

接下来就透过输出的日记,观望该形式被实行与否,来确认对象是还是不是健康被假释。

在各类强引用、弱引用、循环引用、先强后弱引用的坑里跳来跳去后,终于祭出了宏观的杀招,来管理这一个难点:

-(id)key:(NSString *)key
{
    id value=[self.keyValue get:key];
    if(value==nil)
    {
        value=[self.keyValueWeak get:key];
    }
    return value;
}
-(UIView*)key:(NSString *)key valueWeak:(id)value
{
    [self.keyValueWeak set:key value:value];
    return self;
}
-(UIView*)key:(NSString *)key value:(id)value
{
    [self.keyValue set:key value:value];
    return self;
}
-(NSMapTable*)keyValueWeak
{
    NSMapTable *kv=[self.keyValue get:@"keyValueWeak"];
    if(kv==nil)
    {
        kv=[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableWeakMemory];
        [self.keyValue set:@"keyValueWeak" value:kv];
    }
    return kv;
//    NSMapTable *kv= (NSMapTable*)objc_getAssociatedObject(self, &keyValueWeakChar);
//    if(kv==nil)
//    {
//        kv=[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableWeakMemory];
//        objc_setAssociatedObject(self, &keyValueWeakChar, kv,OBJC_ASSOCIATION_RETAIN);
//    }
//    return kv;
}

-(NSMutableDictionary<NSString*,id>*)keyValue
{

    NSMutableDictionary<NSString*,id> *kv= (NSMutableDictionary<NSString*,id>*)objc_getAssociatedObject(self, &keyValueChar);
    if(kv==nil)
    {
        kv=[NSMutableDictionary<NSString*,id> new];
        objc_setAssociatedObject(self, &keyValueChar, kv,OBJC_ASSOCIATION_RETAIN);
    }
    return kv;
}

由此在强引用的Dictionary里,保存三个弱引用NSTableMap,来存档一些索要弱引用的目的,举个例子View或Controller等对象。

正当自己在思量上边的解法的时候:另2个十分的导航栏Crash难点也油可是生了,而且在以下三种状态都会Crash掉:

导航栏命案一:回退即Crash

 1初叶是由此导航栏回退看日志输出,结果却动不动给自个儿来以此:

图片 1

重在还怎么全局断点、僵使对象等方法都不行,只好靠猜〜〜〜〜〜

拍卖那么些标题呢,幸而,作者是把代码折半注释,最终一定到:

图片 2

圈起来的代码,是为每四个UI,都增加多个属性,前一个UI和后三个UI。

消除也很简单,用弱引用存档就好了(今年,强弱引用的难题早就被本人完美化解了):

// Name
- (UIView*)preView{
    return [self key:@"preView"];
}
- (UIView*)preView:(UIView*)view
{
    return [self key:@"preView" valueWeak:view];
}

- (UIView*)nextView{
    return [self key:@"nextView"];
}
- (UIView*)nextView:(UIView*)view
{
    return [self key:@"nextView" valueWeak:view];
}

导航栏命案一:回退即Crash

 一上马是透过导航栏回退看日志输出,结果却动不动给自家来以此:

图片 3

最主要还什么全局断点、僵使对象等办法都于事无补,只好靠猜〜〜〜〜〜

管理这一个题目吗,还好,小编是把代码折半注释,最终牢固到:

图片 4

圈起来的代码,是为每七个UI,都扩张四个天性,前三个UI和后三个UI。

解决也很简短,用弱引用存档就好了(那一年,强弱引用的难题已经被自个儿完美消除了):

// Name
- (UIView*)preView{
    return [self key:@"preView"];
}
- (UIView*)preView:(UIView*)view
{
    return [self key:@"preView" valueWeak:view];
}

- (UIView*)nextView{
    return [self key:@"nextView"];
}
- (UIView*)nextView:(UIView*)view
{
    return [self key:@"nextView" valueWeak:view];
}

导航栏命案二:回退一次即Crash

上一次是援引难题,这二遍啊?笔者X,错误的提示,依旧和上海体育场所壹律,1个main含数里让您猜〜〜〜〜〜

图片 5

并且一级就不乏先例,二级就挂给你看〜〜〜真不要脸。

下一场,就是本着那种灵异事件,各样渡,发现满世界都没出现自己那个主题材料〜〜〜〜那真神了去了。

接下来又起来注释代码,奇妙的意识,在弹回后退时,假诺把上3个状态栏给重置1回,即不会产出Crash现象。

之所以,小编是这般思索导航栏难点的:

    1: 一个navigationCtroller只有一个navigationBar。
    2: 每一个viewController,都会复写前一个navigationBar。
    3: 所以,到下一层时,UINavigationBar指向最后一个Controller
    4: 当回退时,最后一个Controller被释放后,navigationBar没有被重绘的话,事件指向就会出问题。

下一场又起来入手保存当前景色,然后后退时还原状态那条路。

由于后退是自定义事件,所以可以在事件里加代码还原,然而一旦是滑动重返吗?

千身万苦之后,找到:shouldPopItem,在这里能够做点事情:

@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.navigationBar.isHidden)
//        {
//            [current reSetNav:self];
//        }
        if(self.tabBarController!=nil)
        {
            self.tabBarController.tabBar.hidden=![current needTabBar];
        }
    }
}
-(void)dealloc
{
    NSLog(@"UINavigationController relase -> %@", [self class]);
}
@end

改完今后,发掘整个又健康了,然后,又迎来了导航栏的第2波bug。

(PS:shouldPopItem
那么些主意,是第叁个一级Bug坑)

导航栏命案二:回退两回即Crash

上一回是援引难点,那3遍啊?作者X,错误的提醒,依旧和上海体育场面壹律,三个main含数里让你猜〜〜〜〜〜

图片 6

而且拔尖就正常,二级就挂给您看〜〜〜真不要脸。

接下来,正是针对性那种灵异事件,种种渡,开掘全球都没出现本身这么些难题〜〜〜〜那真神了去了。

下一场又发轫注释代码,奇妙的发掘,在弹回后退时,假如把上二个情状栏给重新安装一回,即不会出现Crash现象。

于是,小编是这么思虑导航栏难点的:

    1: 一个navigationCtroller只有一个navigationBar。
    2: 每一个viewController,都会复写前一个navigationBar。
    3: 所以,到下一层时,UINavigationBar指向最后一个Controller
    4: 当回退时,最后一个Controller被释放后,navigationBar没有被重绘的话,事件指向就会出问题。

然后又起来先河保存当前状态,然后后退时还原状态那条路。

出于后退是自定义事件,所以能够在事件里加代码还原,然则只若是滑动再次回到吗?

千身万苦之后,找到:shouldPopItem,在那里能够做点事情:

@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.navigationBar.isHidden)
//        {
//            [current reSetNav:self];
//        }
        if(self.tabBarController!=nil)
        {
            self.tabBarController.tabBar.hidden=![current needTabBar];
        }
    }
}
-(void)dealloc
{
    NSLog(@"UINavigationController relase -> %@", [self class]);
}
@end

改完之后,开采整整又健康了,然后,又迎来了导航栏的第2波bug。

(PS:shouldPopItem
这几个办法,是第叁个一流Bug坑)

导航栏命案三:第贰个页面存在自定义按键,则Crash

以下这一个分界面,右上角一个小相机事件。

图片 7

因为解决难点二的代码中,并未恢复生机第二个页的导航栏,所以事故依然发生了。

接下来又构思,那暗中同意的率先个页面状态怎么保存?

一向保存整个UIButtonBar也许全部NavigationBar,再过来,竟然十二分!!!

大概欲哭无泪,各类渡,仍无果,为什么全球,都没那种难题呢?

难道说全世界写代码都以内部存款和储蓄器不自由,所以木有那么些标题?

这么基础的标题,不是到处都会遇见的么?

然后到了左近小伙的Computer上,把那种美妙的故事,在她Computer上海重机厂新演示了一遍!!!

果真依旧和自家Computer上的一致!!!

果真那难点要么很常见,为何呢,渡不出果呢?

不知底怎么,作为一名新手,他跑去把那段代码给注释了:

//fuck dealloc 方法存在时,会影响导航的后退事件Crash,以下两种情况:1:当前UI有自定义导航按钮时;2:Push两层再回退。
//-(void)dealloc
//{
////    if(self.gestureRecognizers.count>0)
////    {
////        if(self.gestureRecognizers!=nil)
////        {
////            for (NSInteger i=self.gestureRecognizers.count-1; i>=0; i--)
////            {
////                [self removeGestureRecognizer:self.gestureRecognizers[i]];
////            }
////        }
////    }
//    //[self.keyValueWeak removeAllObjects];
//  //  [self.keyValue removeAllObjects];
//
//    NSLog(@"UIView relase -> %@ name:%@", [self class],self.name);
//}

下一场,一切平常了〜〜〜〜咦!!!!

是dealloc里的代码有标题抓住的?

然后中间代码全注释掉了,难点只怕有!!!

把dealloc整个注释掉,又健康了!!!

导航栏命案3:第壹个页面存在自定义开关,则Crash

以下那些分界面,右上角三个小相机事件。

图片 8

因为解决难题2的代码中,并不曾过来第3个页的导航栏,所以事故也许产生了。

下一场又牵挂,那暗中认可的首先个页面状态怎么保存?

平素保存整个UIButtonBar只怕全部NavigationBar,再回复,竟然十分!!!

简直欲哭无泪,各类渡,仍无果,为何全球,都没那种主题材料吗?

难道说全世界写代码都以内部存款和储蓄器不自由,所以木有那些标题?

这么基础的标题,不是四海都会遇见的么?

然后到了相近小伙的Computer上,把那种玄妙的传说,在他计算机上重新演示了一遍!!!

果不其然依旧和作者Computer上的等同!!!

果不其然那题目或然很广泛,为什么呢,渡不出果呢?

不通晓为何,作为一名菜鸟,他跑去把那段代码给注释了:

//fuck dealloc 方法存在时,会影响导航的后退事件Crash,以下两种情况:1:当前UI有自定义导航按钮时;2:Push两层再回退。
//-(void)dealloc
//{
////    if(self.gestureRecognizers.count>0)
////    {
////        if(self.gestureRecognizers!=nil)
////        {
////            for (NSInteger i=self.gestureRecognizers.count-1; i>=0; i--)
////            {
////                [self removeGestureRecognizer:self.gestureRecognizers[i]];
////            }
////        }
////    }
//    //[self.keyValueWeak removeAllObjects];
//  //  [self.keyValue removeAllObjects];
//
//    NSLog(@"UIView relase -> %@ name:%@", [self class],self.name);
//}

接下来,1切日常了〜〜〜〜咦!!!!

是dealloc里的代码有毛病掀起的?

接下来中间代码全注释掉了,难点要么有!!!

把dealloc整个注释掉,又健康了!!!

实质:IOS的Bug,不能够扩充UIView的dealloc方法

到了此处,真相到底出来了,只要扩张了UIView的dealloc方法,导航栏就敢死给你看!!!

笔者了个去,为了查内部存款和储蓄器释放,所以要写dealloc方法,但写了那么些艺术,就引出来这么多奇妙的灵异事件。 

无语啊,那是为了让我们决不管内存释放难题,故意设下的坑么。

IOS除了这几个Bug,还有至极shouldPopItem事件,只要这些事件存在,暗中认可return
YES,就只重临导航头,不会回去分界面,也是个坑!!!

功效是如此的:

图片 9

实质:IOS的Bug,无法扩大UIView的dealloc方法

到了此间,真相终于出来了,只要扩张了UIView的dealloc方法,导航栏就敢死给您看!!!

自个儿了个去,为了查内部存款和储蓄器释放,所以要写dealloc方法,但写了这些点子,就引出来这么多美妙的灵异事件。 

无语啊,那是为了让大家毫不管内部存款和储蓄器释放难点,故意设下的坑么。

IOS除了那个Bug,还有越发shouldPopItem事件,只要那么些事件存在,暗中同意return
YES,就只再次来到导航头,不会回去分界面,也是个坑!!!

效果是如此的:

图片 10

总结:

真是人算不比天算,境遇那种坑,也是全宇宙第3人了。

此地给大伙儿提供贰个坑队友的的新措施:

找个地方,对UIView扩充二个dealloc空方法。

然后就说那Bug很奇妙,让她帮忙看看,包对方头大两尺叁〜〜〜

自然内部存款和储蓄器泄漏的主题材料,到此篇就终止的,但是还有叁个即兴的标题想化解:

期待在block里,大4的写self,也不会导致内存汇漏难题。

又经过4八钟头的奋战,终于消除了。

并且又开掘另2个IOS的坑,可以吗,只能把此文改成人中学,准备再来一篇下。

总结:

当成人算比不上天算,蒙受那种坑,也是全宇宙第四位了。

此地给大伙儿提供3个坑队友的的新措施:

找个地方,对UIView扩张贰个dealloc空方法。

接下来就说那Bug很神奇,让他帮扶看看,包对方头大两尺3〜〜〜

理所当然内部存款和储蓄器泄漏的标题,到此篇就截至的,然则还有一个私行的主题素材想消除:

盼望在block里,肆意的写self,也不会促成内部存款和储蓄器汇漏难点。

又通过4八小时的血战,终于化解了。

再者又开采另1个IOS的坑,好啊,只能把此文字改正成人中学,筹算再来1篇下。

相关文章