IOS视图控制器导航及生命周期研究

1、背景:

2014年4月份第一次接触IOS端开发,为某银行开发一款金融app。
在开发的最后阶段,加入了需要从任意一个页面直接返回主页的功能。
悲催的是,当时没有使用UINavigationController进行导航管理,而是使用了IOS中的模态跳转方式(presentViewController/dismissViewControllerAnimated).

因此需要找的一种方法进行,实现如下方式的导航跳转:

已知: 页面a-->页面b-->页面c

求解: 页面c直接跳回到页面a,并且要求不能有内存泄露,循环依赖等

2、解题思考:

面对上面的需求,最简单的方式是将所有控制器都改成UINavigationController,并且利用pushViewController / popToViewController/ popToRootViewControllerAnimated 等方法进行完美解题。但是当时项目的页面将近100个,分成三大模块,需要大规模修改设计页面以及调整大量代码,这并不是一个现实的解决方案,不到万不得已,不能采取如此低劣手段!

我们需要一个满足如下条件的解决方案:

1) 对于已经在InterfaceBuilder中完成的页面,不做任何修改
2) 尽量少的修改代码,因为很多代码已经经过测试中心测试过,如果修改,需要全部重新测试,时间来不及。

当时通过两天的研究,深入的了解了IOS中的跳转流程和生命周期后,找到了一个相对完美的解决方案,能够满足上面提到的要求。通过一个Demo,来和大家一起分享。

3、解题流程:

1) 为了减少代码的修改,增加一个基类。
@interface BaseViewController : UIViewController

//防止controller循环引用,使用weak引用方式
@property(nonatomic,weak) BaseViewController* parentController;

//为了能够了解某个页面控制器生命周期相关信息,给该控制器取个名字
@property(nonatomic,copy)   NSString*   ctrlName;

//关键的函数,进行页面c-->跳转到主页
-(void) doDismiss;

@end
2) 为了更好的了解IOS中ViewController的生命周期,我们在基类中输出相关信息来了解生命周期相关信息。
@implementation BaseViewController

-(id) init
{
    self.ctrlName = @"";
    self.parentController = nil;
    return [super init];
}

- (void)viewDidLoad
{
    NSLog(@"%@ invoke viewDidLoad",self.ctrlName);
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

-(void)viewWillAppear:(BOOL)animated
{
    NSLog(@"%@ invoke viewWillAppear",self.ctrlName);
    [super viewWillAppear:animated];
}

-(void)viewDidAppear:(BOOL)animated
{
    NSLog(@"%@ invoke viewDidAppear",self.ctrlName);
    [super viewDidAppear:animated];
}

-(void) viewWillDisappear:(BOOL)animated
{
    NSLog(@"%@ invoke viewWillDisappear",self.ctrlName);
    [super viewWillDisappear:animated];
}

-(void) viewDidDisappear:(BOOL)animated
{
    NSLog(@"%@ invoke viewDidDisappear",self.ctrlName);
    [super viewDidDisappear:animated];
}

- (void)didReceiveMemoryWarning
{
    NSLog(@"%@ invoke didReceiveMemoryWarning",self.ctrlName);
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)dealloc
{
    NSLog(@"%@ .......dealloc.......",self.ctrlName);
}
3) 关键的递归函数,核心是理解dismissViewControllerAnimated:completion函数中completion回调的时机点,这个是解题的钥匙。
-(void)doDismiss
{
    NSLog(@"%@ dismiss begin",self.ctrlName);
    
    if([self.parentController isKindOfClass:[ViewController class]])
    {
        [self dismissViewControllerAnimated:YES completion: ^(void)
         {
             NSLog(@"%@ dismiss end",self.ctrlName);
             
             if(self.parentController)
             {
                 [self.parentController doDismiss];
             }
         }];
    }
    else
    {
        [self dismissViewControllerAnimated:NO completion: ^(void)
         {
             NSLog(@"%@ dismiss end",self.ctrlName);
             
             if(self.parentController)
             {
                 [self.parentController doDismiss];
             }
         }];
        
    }
}
4) Demo页面结构如下图: MainViewController-->SecondViewController-->Child1ViewController,然后直接跳回到MainViewController.

5) 所有的ViewController都继承自我们自定义的BaseViewController,具有关键的doDismiss递归方法!
1.png
6) 看一下从MainViewController-->SecondViewController-->Child1ViewController-->MainViewController生命周期的相关信息:

a、启动程序,进入MainViewController:
2017-02-11 17:25:16.964 navTest[933:17086] mainViewCtrl invoke viewDidLoad
2017-02-11 17:25:16.966 navTest[933:17086] mainViewCtrl invoke viewWillAppear
2017-02-11 17:25:16.975 navTest[933:17086] mainViewCtrl invoke viewDidAppear

b、从MainViewController-->SecondViewController:
2017-02-11 17:28:29.717 navTest[933:17086] SecondViewController invoke viewDidLoad [先newController调用viewDidLoad]
2017-02-11 17:28:29.721 navTest[933:17086] mainViewCtrl invoke viewWillDisappear [然后oldController调用viewWillDisappear]
2017-02-11 17:28:29.722 navTest[933:17086] SecondViewController invoke viewWillAppear [然后newController调用viewWillAppear]
2017-02-11 17:28:30.229 navTest[933:17086] SecondViewController invoke viewDidAppear [然后newController调用viewDidAppear ]
2017-02-11 17:28:30.229 navTest[933:17086] mainViewCtrl invoke viewDidDisappear [然后oldController调用viewDidDisappear ]
2017-02-11 17:28:30.230 navTest[933:17086] mainViewCtrl present end
[最后oldController调用presentViewController完成回调被调用]

下面是presentViewController完成回调信息的输出代码,用来了解completion是在哪个阶段被调用的,很重要的信息哦!!!

- (IBAction)ClickToEnterSecondController:(id)sender {
    SecondViewController* ctrl = [self.storyboard instantiateViewControllerWithIdentifier:@"second"];
    ctrl.parentController = self;
    ctrl.ctrlName = @"SecondViewController";
    [self presentViewController:ctrl animated:YES completion:^(void)
     {
         NSLog(@"%@ present end",self.ctrlName);
     }];
}

c、从Child1ViewController直接返回到MainViewController的流程(跳过SecondViewController)

- (IBAction)returnMainViewController:(id)sender {
    NSLog(@"**********************doDismiss*********************");
    [self doDismiss];
}

d、2017-02-11 17:30:30.634 navTest[933:17086] **********************doDismiss********************* [要开始调用doDismiss函数了,表示点击了返回按钮]
2017-02-11 17:30:30.635 navTest[933:17086] Child1ViewController dismiss begin [输出dismiss begin,表示调用了Child1ViewController的doDismiss递归函数]
2017-02-11 17:30:30.637 navTest[933:17086] Child1ViewController invoke viewWillDisappear [oldController viewWillDisappear]
2017-02-11 17:30:30.638 navTest[933:17086] SecondViewController invoke viewWillAppear [newController viewWillAppear]
2017-02-11 17:30:30.641 navTest[933:17086] SecondViewController invoke viewDidAppear [newController viewDidAppear]
2017-02-11 17:30:30.641 navTest[933:17086] Child1ViewController invoke viewDidDisappear [oldController viewDidDisappear ]
2017-02-11 17:30:30.641 navTest[933:17086] Child1ViewController dismiss end [关键时刻哦,oldController self dismissViewControllerAnimated: completion: 中的completion 回调被触发了,它是在 oldController viewDidDisappear后被触发的哦]
2017-02-11 17:30:30.641 navTest[933:17086] SecondViewController dismiss begin [这时候,从ChildViewController回弹到SecondViewController的流程完成了,接下来递归调用,要完成从SecondViewController回弹到MainViewController的过程,重复上面的流程而已]
2017-02-11 17:30:30.642 navTest[933:17086] Child1ViewController .......dealloc....... [Child1ViewController已经完全消失了,因此内存被析构了,这样确保内存不会泄露哦]

下面是递归部分,和上面流程一样,只是从SecondViewController回跳到MainViewController

2017-02-11 17:30:30.643 navTest[933:17086] SecondViewController invoke viewWillDisappear
2017-02-11 17:30:30.644 navTest[933:17086] mainViewCtrl invoke viewWillAppear
2017-02-11 17:30:31.156 navTest[933:17086] mainViewCtrl invoke viewDidAppear
2017-02-11 17:30:31.156 navTest[933:17086] SecondViewController invoke viewDidDisappear
2017-02-11 17:30:31.156 navTest[933:17086] SecondViewController dismiss end
2017-02-11 17:30:31.157 navTest[933:17086] mainViewCtrl dismiss begin
2017-02-11 17:30:31.157 navTest[933:17086] SecondViewController .......dealloc....... [SecondViewController 内存也很完美的被析构掉,目前仅剩下MainViewController还活着]

show一下某银行APP以前模拟器中的效果截图,IOS开发还是非常令人非常愉悦的感觉!


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

推荐阅读更多精彩内容