loadView和viewDidLoad及initWithNibName/awakeFromNib/initWithCoder

本文详细解释了iOS视图控制器中的关键方法loadView、viewDidLoad和viewDidUnload的作用及使用场景,帮助开发者理解视图的加载、初始化和卸载过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每个ios开发者对loadViewviewDidLoad肯定都很熟悉,虽然这两个函数使用上真的是非常简单,但是和类似的initWithNibName/awakeFromNib/initWithCoder放在一起还是非常容易让人混淆的.

一、loadView

永远不要主动调用这个函数。view controller会在view的property被请求并且当前view值为nil时调用这个函数。如果你手动创建view,你应该重载这个函数。如果你用IB创建view并初始化view controller,那就意味着你使用initWithNibName:bundle:方法,这时,你不应该重载loadView函数。

这个方法的默认实现是这样:先寻找有关可用的nib文件的信息,根据这个信息来加载nib文件,如果没有有关nib文件的信息,默认实现会创建一个空白的UIView对象,然后让这个对象成为controller的主view。

所以,重载这个函数时,你也应该这么做。并把子类的view赋给view属性(property)(你create的view必须是唯一的实例,并且不被其他任何controller共享),而且你重载的这个函数不应该调用super。

如果你要进行进一步初始化你的views,你应该在viewDidLoad函数中去做。在iOS 3.0以及更高版本中,你应该重载viewDidUnload函数来释放任何对view的引用或者它里面的内容(子view等等)。

这个网上的资料都说的很不全面,尤其是蓝色字部分。

二、viewDidLoad

这个函数在controller加载了相关的views后被调用,而不论这些views存储在nib文件里还是在loadView函数中生成。而多数情况下是做nib文件的后续工作。

网上资料对这个函数的描述则完全不对。

三、viewDidUnload

这个函数是viewDidLoad的对立函数。在程序内存欠缺时,这个函数被controller调用,来释放它的view以及view相关的对象。由于controller通常保存这view以及相关object的引用,所以你必须使用这个函数来放弃这些对象的所有权以便内存回收。但不要释放那些难以重建的数据。

通常controller会保存nib文件建立的views的引用,但是也可能会保存着loadView函数创建的对象的引用。最完美的方法是使用合成器方法:

1
self .myCertainView = nil ;

这样合成器会release这个view,如果你没有使用property,那么你得自己显示释放这个view。

网上对这个函数的描述含含糊糊,看了等于没看。

四、结论

所以流程应该是这样:

(loadView/nib文件)来加载view到内存 ——>viewDidLoad函数进一步初始化这些view ——>内存不足时,调用viewDidUnload函数释放views

—->当需要使用view时有回到第一步

如此循环

    需要厘清两个概念,创建一个类和实例化一个类.在XCode中创建一个类和实例化一个类很容易区分,但是在IB(Interface Builder)中有时候就会迷糊.其实也很好区分,孤零零地创建了一个nib文件,没有和其他可被实例化的类有直接或间接关系的时候,这个类或这些类(一个nib文件俺也可能包含多个类)是没有机会被实例化的,所以这种情况只是通过ib创建了一个类,而没有实例化.真正的实例化还需要通过在Xcode用代码来读取这个nib文件.知道这两这的区别后这些方法也就容易辨认多了

viewDidLoad其实没什么可混淆的,无论通过什么途径加载(Xcode或者IB,这里的加载属于实例化)完view后肯定会执行这个方法.

loadView需要分两种情况.当你通过Xcode实例化一个类的时候就需要自己在controller中实现这个方法.而在IB中实例化就不需要实现它.

initWithNibName这个方法是在controller的类在IB中创建,但是通过Xcode实例化controller的时候用的.

awakeFromNib这个方法是一个类在IB中被实例化是被调用的.看了帖子发现大家都推荐使用viewDidLoad而不要使用awakeFromNib,应为viewDidLoad会被多次调用,而awakeFromNib只会当从nib文件中unarchive的时候才会被调用一次.实际测试中发现,当一个类的awakeFromNib被调用的时候,那么这个类的viewDidLoad就不会被调用了,这个感觉很奇怪.

initWithCoder是一个类在IB中创建但在xocdde中被实例化时被调用的.比如,通过IB创建一个controller的nib文件,然后在xocde中通过initWithNibName来实例化这个controller,那么这个controller的initWithCoder会被调用.

awakeFromNib 
当.nib文件被加载的时候,会发送一个awakeFromNib的消息到.nib文件中的每个对象,每个对象都可以定义自己的awakeFromNib函数来响应这个消息,执行一些必要的操作。也就是说通过nib文件创建view对象是执行awakeFromNib 

viewDidLoad 
当view对象被加载到内存是就会执行viewDidLoad,所以不管通过nib文件还是代码的方式创建对象都会执行viewDidLoad 

<think>我们正在讨论iOS开发中UIViewController的viewDidLoad方法。根据引用[1]引用[2],viewDidLoad是UIViewController生命周期中的一个重要方法。 引用[1]指出:无论是从xib文件加载视图还是自己创建视图,viewDidLoad方法都会被调用,而且是在视图已经被加载后调用。同时强调,任何UI控件的创建都不应该在viewDidLoad方法中实现,因为这是xib文件或者loadView方法的任务。viewDidLoad只是通过nib或者loadView方法加载视图的一种拓展补充机制。 引用[2]详细描述了UIViewController的生命周期,其中viewDidLoadloadView之后被调用,此时整个视图层次(view hierarchy)已经放到内存中,可以移除一些视图,修改约束,加载数据等。 因此,我们可以总结如下: 1. viewDidLoad的调用时机:在视图控制器的视图被加载到内存之后调用,且在整个视图控制器的生命周期中只调用一次(除非视图被从内存中清除,但通常不会)。 2. 作用:用于进行一些初始化操作,但这些操作不应该包括创建视图层次(因为视图层次已经在loadView或从nib/storyboard加载完成),而应该是对已经加载的视图进行配置,例如设置控件的属性、加载数据、添加手势等。 3. 注意事项:避免在viewDidLoad中执行耗时操作,因为这会延迟视图的显示。如果确实需要执行耗时操作,应该使用异步方式。 使用场景: - 配置界面上的控件(例如设置文本、颜色、字体等) - 初始化数据模型,并更新界面 - 添加手势识别器 - 注册通知 - 设置委托 代码示例: ```objective-c - (void)viewDidLoad { [super viewDidLoad]; // 设置标题 self.title = @"首页"; // 设置背景色 self.view.backgroundColor = [UIColor whiteColor]; // 配置一个标签 UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 200, 40)]; label.text = @"Hello, World!"; [self.view addSubview:label]; // 加载数据 [self loadData]; } - (void)loadData { // 异步加载数据 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 模拟网络请求 [NSThread sleepForTimeInterval:2.0]; dispatch_async(dispatch_get_main_queue(), ^{ // 更新UI }); }); } ``` 注意:在viewDidLoad中,视图的几何属性(如frame、bounds)可能还没有确定,因为此时还没有进行自动布局(Auto Layout)的计算。因此,如果代码依赖于视图的尺寸,最好在viewDidLayoutSubviews中处理。 另外,引用[1]特别强调,UI控件的创建不应该在viewDidLoad中,而应该在loadView方法中(如果是通过代码创建视图)或者由xib/storyboard完成。viewDidLoad只是对已经存在的视图进行配置。 总结:viewDidLoad是进行视图控制器初始化设置的重要方法,但要注意它只被调用一次,并且此时视图的几何属性可能还未确定。 相关问题: 1. viewDidLoadloadView方法有什么区别? 2. 在viewDidLoad中为什么不能设置视图的frame? 3. 如果视图控制器是从storyboard创建的,viewDidLoad中应该做什么? 4. viewDidLoadviewWillAppear的区别是什么? 5. 在viewDidLoad中执行网络请求是否合适?</think>### iOS开发中 `viewDidLoad` 方法详解 `viewDidLoad` 是 `UIViewController` 生命周期中的核心方法,在视图加载完成后触发。以下是详细解析: #### 1. **触发时机与作用** - **触发时机**:视图控制器完成视图加载后调用(在 `loadView` 方法之后)[^1][^2] - **关键特征**: ```mermaid graph LR A[initWithNibName] --> B[loadView] B --> C[viewDidLoad] C --> D[viewWillAppear] ``` - **作用**: - 执行**一次性初始化**操作 - 配置视图元素属性(颜色、字体等) - 加载初始数据 - 注册通知/委托 #### 2. **正确使用场景** ```objective-c - (void)viewDidLoad { [super viewDidLoad]; // ✅ 正确场景1:配置视图属性 self.titleLabel.textColor = [UIColor redColor]; self.tableView.delegate = self; // ✅ 正确场景2:初始化数据 [self loadInitialData]; // ✅ 正确场景3:添加手势识别 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; [self.view addGestureRecognizer:tap]; // ✅ 正确场景4:注册通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; } ``` #### 3. **禁止操作与常见误区** - ❌ **禁止创建视图**: ```objective-c // 错误示例:创建新视图 UIView *newView = [[UIView alloc] init]; // 应在loadView中创建[^1] ``` - ❌ **避免布局操作**: ```objective-c // 错误示例:设置视图尺寸 self.button.frame = CGRectMake(0, 0, 100, 40); // 应在viewDidLayoutSubviews中处理[^2] ``` - ⚠️ **注意点**: - 每个视图控制器**仅调用一次**(除非手动释放视图) - 视图的几何属性(frame/bounds)尚未确定 - 不保证网络请求完成时界面已显示 #### 4. **与其他生命周期方法对比** | 方法 | 调用次数 | 典型用途 | 视图几何状态 | |---------------------|----------|-----------------------------------|--------------------| | `loadView` | 1次 | 创建视图层次 | 未设置 | | **`viewDidLoad`** | 1次 | 配置视图属性/加载数据 | 未确定 | | `viewWillAppear` | 多次 | 刷新数据/准备动画 | 已确定 | | `viewDidLayoutSubviews` | 多次 | 最终布局调整 | 最终确定 | #### 5. **最佳实践建议** 1. **保持轻量化**: ```objective-c // 耗时操作异步执行 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *data = [NSData dataWithContentsOfURL:imageURL]; dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = [UIImage imageWithData:data]; }); }); ``` 2. **内存管理**: ```objective-c - (void)dealloc { // 必须移除观察者 [[NSNotificationCenter defaultCenter] removeObserver:self]; } ``` 3. **状态恢复**: ```objective-c - (void)encodeRestorableStateWithCoder:(NSCoder *)coder { [super encodeRestorableStateWithCoder:coder]; [coder encodeObject:self.dataArray forKey:@"data"]; } ``` #### 6. **调试技巧** ```objective-c - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@: viewDidLoad", self.class); // 检查视图层次 NSLog(@"View hierarchy: %@", self.view.subviews); } ``` > **关键总结**:`viewDidLoad` 是视图初始化的**核心枢纽**,适合执行**一次性配置**,但需避免视图创建布局操作。它确保视图层次已加载,但尚未显示到屏幕[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值