iOS小记--调用pop方式返回,但是当前控制器没有被释放的解决方式

一直不怎么习惯使用一些门户博客,平日里开发时遇到的问题就随手记到印象笔记里.
但是在今天下午真的是遇到了一个令人悲伤的BUG.
我在一个控制器里:

通知监听页(B)

注册了几个通知的监听方法,其中一个通知只在这个控制器(B)里注册了监听,然后在下一个控制器(C)发送通知, 通知的post方只有一个,通知的observer方也只有一个,并没有在其他类里出现这个通知.

支付类型页(C)

由于一些设计上的需求,我需要:

1.在更换支付类型页面(C)发送通知,然后回到B页刷新数据接口,
2.并且当在B页执行 返回/修改商品 操作时,由B页pop回购物车控制器(A).

那么问题来了:

1.当我pop掉B回到A的时候,看似B是从栈里移除掉了,然而当广播发来的时候,B依旧响应了监听方法,这就有点尴尬了,
2.当我在B里进行了N次频繁的修改商品pop回A的操作时,再从购物车控制器(A)push到B里,最后进入C里面更换支付类型发送通知给B,到了B页面接收通知刷新数据,发现B页面刷新数据的监听方法被频繁调用了N次!并且因为商品在被不停修改,只有一次的刷新数据请求返回值是正确的,其他的全是错误的..这TM就很尴尬了...

其实这两个问题是一个问题,只不过第问题2更为复杂一些.原因是当B执行pop操作时,根本没有走到dealloc 方法,从而没有去具体执行B的销毁工作.

ARC下控制器在被pop后移出栈后会被释放,但有些时候会发现控制器出栈的时候不会调用dealloc方法,归根结底,是因为当前控制器被某个对象强引用了,控制器的引用计数不为0,系统无法帮你释放这部分内存。原因大致有以下几点:

1.控制器中NSTimer没有被销毁

当viewController中存在NSTimer时,需要特别注意,当调用

[NSTimerscheduledTimerWithTimeInterval:1.0target:selfselector:@selector(updateTime:) userInfo:nilrepeats:YES];

时,因为 target:self ,也就是引用了当前viewController,导致控制器的引用计数加1,如果没有将这个NSTimer 销毁,它将一直保留该viewController,无法释放,也就不会调用dealloc方法。所以,需要在viewWillDisappear之前需要把控制器用到的NSTimer销毁。

[timer invalidate];//销毁
timertimer =nil;//置nil

2.viewController中的代理不是weak属性

例如@property (nonatomic, weak) id delegate;代理要使用弱引用,因为自定义控件是加载在视图控制器中的,视图控制器view对自定义控件是强引用,如果代理属性设置为strong,则意味着delegate对视图控制器也进行了强引用,会造成循环引用。导致控制器无法被释放,最终导致内存泄漏。

3.viewController中block的循环引用

在ARC下,block会把它里面的所有对象强引用,包括当前控制器self,因此有可能会出现循环引用的问题。比如viewController中有个block属性,在block中又强引用了self或者其他成员变量,那么这个viewController与自己的block属性就形成循环引用,导致viewController无法释放。

1和2 的情况很好检查,你自己的控制器里有没有用 **NSTimer和代理 **你自己很清楚,检查后没有就只有第三种情况了.

第三种情况需要我们仔细跟踪自己的代码来检查是否是出现了循环引用的问题.但是当我们跟踪完之后发现block块代码里没有循环引用的问题时,你会觉得确实TM尴尬...怎么办呢 ? 这里答题说一下我的解决办法.

其实问题是出在当我们从B pop回去的时候,由于强引用问题,B一直没有被销毁,而存在于内存中,我们频繁不停的修改商品后会出现N次B--A--B之间的pop<-->push,会导致内存里产生了N个控制器B的实例.所有的B由于有没有查询到的强引用而一直没有被销毁,所以才会在C发送通知时,已经有了N个B实例了,每一个实例里面的observer观察者都监听到了post,于是触发了N个实例里面的N遍监听响应方法.

但是在导航控制器的栈里面,只要pop出去了,B就会在这个栈里面被移除掉,也就是说在这个场景内,navgation导航条的栈内最多只有一个B的实例存在,并且这个实例(当前的B)就是我们需要的,会显示在屏幕上的那个. 剩下的,我们只需要拿到这个实例,只给这一个实例去做请求去更新数据就可以了.我们只需要在执行observer的监听方法时,只执行这个当前的B的数据更新请求就可以了.下面是具体的做法:

-(void)viewDidLoad{
    [super viewDidLoad];
    self.title = @"确认下单";
    
    //UI
    [self.view addSubview:self.tableView];
    [self.view addSubview:self.bottomView];
    
    //Data
    [self loadData];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDataSource:) name:@"changePayMethod" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateAgreementIdIfTurnEasy:) name:@"addAgreementIdIfTurnEasy" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateAddressModel:) name:@"comfimOrderUpdateAddressModel" object:nil];
 }


-(void)updateDataSource:(NSNotification *)notif{
    
    WEAKSELF
    [self.navigationController.childViewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[XXConfirmOrderController class]]) {  //当前屏幕上的确认下单页
            if (obj == weakSelf) {

                //1.刷新支付方式
                NSString *payMethod = notif.object;
                [weakSelf.confimModel setPayMethod:payMethod];
                
                //2.根据支付方式刷新价格
                [weakSelf updateAllPriceByPayType];
            }
        }
    }];
    
    
}


-(void)updateAllPriceByPayType{
    
    NSMutableDictionary *dyParam = [NSMutableDictionary dictionary];
    NSString *payType = [NSString stringWithFormat:@"%zd",self.confimModel.orderType];
    [dyParam setValue:payType forKey:@"payType"];
    [dyParam setObject:@"cartGoods" forKey:@"from"];    //来自于购物车的标识符

    __block NSMutableArray *cartGoodsMapList = [NSMutableArray array];
    [self.goodsModelArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        XXCartGoodMdel *model = obj;
        NSString *goodsId = model.goodId;
        NSString *goodsName = model.goodsName;
        NSNumber * goodsNumber = [NSNumber numberWithInt:model.goodsNumber];    //要number格式
        NSString *isChecked = model.isChecked;
        NSString *discountId = model.discountId ? model.discountId : @"";
        NSString *serviceIds = model.serviceIds ? model.serviceIds : @"";
        NSString *isChangeGoodsFlag = model.isChangeGoodsFlag;
            
        NSMutableDictionary *goodDic = [NSMutableDictionary dictionaryWithDictionary:@{@"userId":[AccountTool account].userId ,@"goodsId":goodsId, @"goodsNumber":goodsNumber, @"isChangeGoodsFlag":isChangeGoodsFlag,@"goodsName":goodsName,@"isChecked":isChecked, @"discountId":discountId, @"serviceIds":serviceIds }];
            [cartGoodsMapList addObject:goodDic];
    }];
        
    [dyParam setValue:cartGoodsMapList forKey:@"cartGoodsMapList"];
    [dyParam setObject:[NSString stringWithFormat:@"%@",self.cartModel.original] forKey:@"original"];
    [dyParam setObject:[NSString stringWithFormat:@"%@",self.cartModel.cut] forKey:@"cut"];
    [dyParam setObject:[NSString stringWithFormat:@"%@",self.cartModel.total] forKey:@"total"];
    
    WEAKHUD
    WEAKSELF
    [[APIClientFactory sharedManager] requestPriceOfCartByPayTypeWithDynamicParams:dyParam success:^(Response *response) {
        [weakHud hideAnimated:YES];
        NSDictionary *dic = (NSDictionary *)response.data;
        NSNumber *total = [dic objectForKey:@"total"];
        if (response.code == 2) {
            [JXTAlertTools showTipAlertViewWith:weakSelf title:@"提示" message:response.message buttonTitle:@"知道了" buttonStyle:JXTAlertActionStyleDefault];
        }
        
        [weakSelf.confimModel setAllPrice:total.floatValue];
        
        [weakSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:2 inSection:0],[NSIndexPath indexPathForRow:3 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
        [weakSelf.bottomView setModel:weakSelf.confimModel];
    } failure:^(NSError *error) {
        [weakHud hideAnimated:YES];
        [HUD showError:error.userInfo[@"message"] toView:weakSelf.view];
    }];

}

做完这个处理之后,就能很好的规避到重复进行N次数据刷新请求了,只会保留到我们所看到的屏幕上的当前B页的通知响应.当然这种处理暂时没有办法规避内存中被强引用的其它实例的销毁问题,想处理的话还需要我们进一步细心去跟踪.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 序言 目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了。今年,找过工作人可能会更深刻地体会到今年的就...
    独酌丿红颜阅读 2,357评论 18 60
  • http://blog.csdn.net/lcg910978041/article/details/51468821
    liu_bo阅读 77评论 0 0
  • idDom: 传入容器id,这是必传项,用于指定画布的domwidth和height: 容器的宽高textColo...
    YomonAh阅读 3,098评论 3 2