本文共 9661 字,大约阅读时间需要 32 分钟。
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder@available(iOS 2.0, *) public class UIViewController : UIResponder, NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment
视图控制器负责页面的创建、事件处理等。
每一个视图控制器(UIViewController)内部都有个默认的 UIView 属性,控制器中管理的其他所有控件都是这个 view 的子控件(直接或者间接)。
Objective-C
// 实例化视图控制器 UiViewController *viewController = [[UiViewController alloc] init]; // 设置 window 的根视图控制器 self.window.rootViewController = viewController;
Swift
// 实例化视图控制器 let viewController:UiViewController = UiViewController() // 设置 window 的根视图控制器 self.window?.rootViewController = viewController
Objective-C
// 设置 viewController 的背景颜色 self.view.backgroundColor = [UIColor redColor]; // 向 viewController 上添加视图 [self.view addSubview:button]; // 向 viewController 上添加视图控制器 [self addChildViewController: viewController2]; [self.view addSubview:viewController2.view];
Swift
// 设置 viewController 的背景颜色 self.view.backgroundColor = UIColor.redColor() // 向 viewController 上添加视图 self.view.addSubview(button) // 向 viewController 上添加视图控制器 self.addChildViewController(viewController2) self.view.addSubview(viewController2.view)
Objective-C
// 跳转到 指定 页面 [self presentViewController:viewController1 animated:YES completion:nil]; // 返回到 上一个 页面 [self dismissViewControllerAnimated:YES completion:nil];
Swift
// 跳转到 指定 页面 self.presentViewController(viewController1, animated: true, completion: nil) // 返回到 上一个 页面 self.dismissViewControllerAnimated(true, completion: nil)
相关方法执行顺序:
init -> loadView -> viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisappear -> viewDidDisappear -> viewWillUnload -> viewDidUnload —> dealloc
分配内存 -> 加载视图 -> 视图已经加载 -> 视图将要出现 -> 将要布局子视图 -> 已经布局子视图 -> 视图已经出现 -> 视图将要消失 -> 视图已经消失 -> 将要销毁视图 (iOS 6+ 已废弃)-> 已经销毁视图(iOS 6+ 已废弃) -> 释放内存
viewController 对 view 加载过程:
loadView
- (void)loadView; public func loadView()
awakeFromNib
- (void)awakeFromNib; public func awakeFromNib()
viewDidLoad
- (void)viewDidLoad; public func viewDidLoad()
用这个的时候,ViewController 已经完全好了,outlet 也已经连接好了。但是还没有在屏幕上显示出来。这个方法里面可以放很多设置的代码。这个方法执行的时候,view 的 bounds 还没有。先 load,再 appear。
视图加载完成后执行,可以做一些数据初始化的工作,如果用纯代码开发,不要在此方法中设置界面 UI。如果是 Storyboard 开发,可以动态添加一些控件,以及加载数据。
viewWillAppear
- (void)viewWillAppear:(BOOL)animated; public func viewWillAppear(animated: Bool)
这个方法调用的时候,视图的 bounds 已经有了。
视图只会 loaded 一次,但是会 appear 或者 disappear 很多次。不变的东西,放在 viewDidLoad 里面。和几何相关的,放在 viewWillAppear 里面。这点对项目的优化很重要。就好似顶层的 view,旋转 ipad 什么的都需要改变顶层的 view 的大小,当一个 ViewController 的生命周期到这里的时候,就可以在这里的最后时刻来调整 view 的排列或者几何特性。
这里也设置做一些 lazy execution for performance。比如:需要按一个 button,出现一个 view 什么的。这里设置,开销很大。耗时很长的事情最好在 viewWillAppear 里另开一个线程运行,然后在 view 里面放一个小小的 spinning wheel。
viewWillLayoutSubviews
- (void)viewWillLayoutSubviews; public func viewWillLayoutSubviews()
这个方法专门用来布局子控件,一般在这里设置子控件的 frame,当控件本身的尺寸发生改变的时候,系统会自动调用这个方法。
layoutSubviews 在以下情况下会被调用:
viewWillDisappear
- (void)viewWillDisappear:(BOOL)animated; public func viewWillDisappear(animated: Bool)
Objective-C
// 纯代码加载视图 - (void)loadView { [super loadView]; } // 从 Nib 加载视图 - (void)awakeFromNib { [super awakeFromNib]; } // 视图已经加载 - (void)viewDidLoad { [super viewDidLoad]; } // 视图将要出现,在 viewDidLoad 执行完成之后执行 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } // 将要布局子视图 - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; } // 已经布局子视图 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; } // 视图已经出现 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } // 视图将要消失 - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } // 视图已经消失,不是销毁 - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; }
Swift
// 纯代码加载视图 override func loadView() { super.loadView() } // 从 Nib 加载视图 override func awakeFromNib() { super.awakeFromNib() } // 视图已经加载 override func viewDidLoad() { super.viewDidLoad() } // 视图将要出现,在 viewDidLoad 执行完成之后执行 override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) } // 将要布局子视图 override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() } // 已经布局子视图 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() } // 视图已经出现 override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) } // 视图将要消失 override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) } // 视图已经消失,不是销毁 override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) }
通过代码为 xib 或 storyboard 中 view 增加约束时,尽量避免在 viewDidLoad 中执行,最好放在 updateViewConstraints(UIViewController 中) 或者 updateConstraints(UIView 中)中,记得调用 [super updateViewConstraints] 或者 [super updateConstraints];
如果你真的写在 viewDidLoad 里了,那么可能会遇到这种崩溃错误 Terminating app due to uncaught exception "NSInternalInconsistencyException"。
代码设置约束时,需设置该 view 的 translatesAutoresizingMaskIntoConstraints 属性已设置为 NO。
Objective-C
// 更新视图控制器中视图的布局约束 - (void)updateViewConstraints { // 在这里为你的 view 添加约束 [super updateViewConstraints]; } // 更新视图布局约束 - (void)updateConstraints { // 在这里为你的 view 添加约束 [super updateConstraints]; }
Swift
// 更新视图控制器中视图的布局约束 override func updateViewConstraints() { // 在这里为你的 view 添加约束 super.updateViewConstraints() } // 更新视图布局约束 override func updateConstraints() { // 在这里为你的 view 添加约束 super. updateConstraints() }
iPhone 下每个 app 可用的内存是被限制的,如果一个 App 使用的内存超过 20M,则系统会向该 App 发送 Memory Warning 消息。苹果公司系统工程师建议,应用程序所占内存不应该超过 20MB,开发人员圈内流传着一个粗略的经验法则:当应用程序占用了大约 20MB 内存时,iPhone 开始发出内存警告。收到消息后 App 必须尽可能多的释放一些不必要的内存,当应用程序所占内存大约为 30MB 时,iPhone OS 会关闭应用程序。收到此消息后,App 必须正确处理,否则可能出错或者出现内存泄露。App 收到 Memory Warning 后会调用:UIApplication::didReceiveMemoryWarning -> UIApplicationDelegate::applicationDidReceiveMemoryWarning,然后调用当前所有的 viewController 进行处理。因此处理的主要工作是在 viewController。
内存警告处理思路
iOS 5 的处理
在 iOS 6 之前,如果应用程序接收到了 low-memory 警告,当前不可见的 viewController 会接收到 viewDidUnload 消息(也可以理解为自动调用 viewDidUnload 方法),所以我们需要在 viewDidUnload 方法中释放掉所有 outlets ,以及可再次创建的资源。当前可见的 viewController 通过 didReceiveMemoryWarning 合理释放资源。
有这样一个 viewController。
@interface MyViewController : UIViewController { NSArray *dataArray; } @property (nonatomic, strong) IBOutlet UITableView *tableView; @end
对应的处理为。
- (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Relinquish ownership any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand. // For example: self.myOutlet = nil; self.tableView = nil; dataArray = nil; [super viewDidUnload]; }
iOS 6+ 的处理
iOS 6 废弃了 viewDidUnload 方法,这就意味着一切需要我们自己在 didReceiveMemoryWarning 中操作。
1、将 outlets 置为 weak。当 view dealloc 时,没有人握着任何一个指向 subviews 的强引用,那么 subviews 实例变量将会自动置空。
@property (nonatomic, weak) IBOutlet UITableView *tableView;
2、在 didReceiveMemoryWarning 中将缓存数据置空。不要忘记一点,每当 tableview reload 的时候,需要判断一下 dataArray ,若为空则重新创建。
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. dataArray = nil; }
兼容 iOS 5 与 iOS 6+
倘若希望程序兼容 iOS 5 与 iOS 6+ 怎么办呢?这里有一个小技巧,我们需要对 didReceiveMemoryWarning 做一些手脚。
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; if (self.isViewLoaded && self.view.window == nil) { self.view = nil; } dataArray = nil; }
转载地址:http://vjnbx.baihongyu.com/