Objective- C之Nib Files

·  阅读 839
Objective- C之Nib Files

1. 前言

mac os app 和 ios app开发都离不开UI的制作,在UI制作方式上可以分为两类:代码手写UI及布局和使用单个xib文件组织viewController或者view。本文不是介绍这两种制作UI方式的优劣,而是介绍xib文件组织viewController之后,load nib文件之后"发生了什么"。如果需要知道对于这两种方式制作UI的缺点,可以参看猫神的这两篇问哈那个代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧再看关于 Storyboard 的一些争论。或者阅读这篇A Case For Using Storyboards on iOS英文文章。

2. 从一行代码聊起

我们先来看下面这个创建一个viewController对象的代码。

//method description
/*Returns a view controller object initialized to the nib file in the specified bundle. */

myViewController = [myViewController initWithNibName:@"myViewController" bundle:nil];
复制代码

该方法的声明如下:

- (instancetype)initWithNibName:(NSNibName)nibNameOrNil 
                         bundle:(NSBundle *)nibBundleOrNil;

nibNameOrNil(typedef NSString *NSNibName): The name of the nib file, without any leading path information.

nibBundleOrNil:The bundle in which to search for the nib file. If you specify `nil`, this method looks for the nib file in the main bundle.
复制代码

创建一个myViewController类的对象,从指定的bundle中寻找名为myViewController的nib file,该nib file的owner必须设置成(在Xcode中)myViewController。通过组织xib文件来对你的view,window或者其他UI控件进行布局。(xib和nib可以理解为一个东西,可以参考What is a nib?)。 通过Xcode建立xib文件,在上面建立对象,并对一些配置进行设定(比如宽高,背景颜色和View之间的Constraint等)那么当load xib文件,这些对象可以在内存中实例化,并且在Xcode中进行的配置也能够复制。

3. 剖析xib文件

在一个xib文件中,可以包含多种类型的对象,这一小节就介绍xib文件中不同类型的对象。

3.1 Interface Objects

interface objects是一些用户界面对象,比如windows,views,controles和menus。比如下面这张图,在myViewController.xib文件中就是一些可视化对象。

截屏2022-01-01 19.21.44.png 除了可视化对象可以被添加至xib文件中,另外非可视化对象也可以添加到xib文件中,比如controller objects。想了解controller objects可以参考文档。这些controller object可以管理可视化对象,当然controller object也可以通过代码创建。下面这张图的xib文件中,创建了Popover View Controller对象,它的作用是管理Popover这个可视化对象。

截屏2022-01-01 19.38.19.png

3.2 File's Owner

File‘s Owner Object对象在xib文件中最重要的对象那个,不同于interface object。File‘s Owner Object是一个placeholder object,当load xib文件时,它并不会被创建。实际上,当load xib file时,需要我们向其传递一个object作为File‘s Owner Object。不过此时,我想介绍下在Xcode中xib file中的一个File‘s Owner选项,如下图所示。 截屏2022-01-01 19.46.44.png 可以看出在最右上角,将File's Owner的Class设置成myViewController。那么此时你将interface object的outlet拖着至File‘s Owner可以看出有几个选项。

截屏2022-01-01 19.50.24.png 其中mainView我们在myViewController.m文件中有定义。

截屏2022-01-01 19.21.55.png 这个对象重要的原因是它是你的代码和xib文件之间的连接桥梁,当你load xib文件,这个load xib文件的代码重新建立这些连接关系(这个后面会讲到)。

3.3 Top-Level Objects

当load xib文件后,CoCoa会重新建立你在XCode中建立的graph of objects。这个object graph包含了所有的你在xib文件中创建的windows,views以及custom objects。那么这个Top Level Objects指的是这些对象的子集,并且没有parent object.一般来说top level objects包括windows,menu bars和custom controller objects(File‘s Owner object不属于Top Level Object)。

3.4 Image and Sound Resource

在Xcode中可以使用图像和音频资源,比如ImageView的内容可以指定图片,比如下面这样,在xib文件中,增加了一个ImageView,并且它使用了图片资源。Xcode的库提供了对图片和音频资源的访问的功能,可以讲xib文件链接到这些资源。xib文件并不直接存储资源,它指存储文件的名称,为的是load xib文件后可以找到这些资源。

截屏2022-01-01 20.09.47.png

4. Nib File Design Guidelines

当你创建xib文件时,你需要思考如何使用xib文件中建立的对象。尽管一个非常简单的应用程序可以讲所有的用户界面对象存储到一个单独的xib文件,但是对于大多数应用程序,利用多个xib文件来组织的你的用户界面。这起码有两个好处:创建小的xib文件可以让你只是加载所需的部分,带来速度上的提升。并且当你出现问题,可以方便的定位出问题。当你创建xib文件时,你可以遵循下面几个原则:

  • 设计的xib文件要考虑到延迟加载,就是指只加载你所需的对象的那个xib文件。
  • 在OS X应用程序的main xib文件中,需要考虑xib文件应该只存储应用程序菜单和以及delegate object。不需要启动之后不会使用的任何windows或user-interface elements。
  • 将重复使用的用户界面组件存储在一个单独的xib文件中。
  • 偶尔使用的windows或menu,可以将其单纯存储到一个单独的xib文件中,当需要时才加载到内存中。
  • File‘s Owner成为xib file中的对象与不在xib file之外的对象之间进行链接的单一对象。

5 重回Load Xib File

当一个xib file加载到内存中,nib-loading code会有几个步骤去确保xib file中的对戏那个能够正确创建和初始化。知道这些步骤有助于你更好的写出controller code来管理的用户界面对象。其流程如下图所示:

未命名文件.png

  1. 将xib flie文件内容和所引用的资源(比如图像和音频)加载到内存中。
  • xib file整个object graph的原始数据被加载到内存中,但是没有被unarchived。(note:所有在xib文件创建的对象可以看作是被encoded,需要对应的decode方法进行unarchive,特别需要注意,一个xib文件在磁盘上是以xib文件形式存在,但是加载在内存中是以一个NSNib类的实例对象存在。)
  • 图像资源被加载到内存中
  • 音频资源被加载到内存中
  1. unarchives nib object graph原始数据和实例化对象。初始化每一个对象是根据它是如何被enoded in the archive。主要有如下几个原则:
  • 默认情况下,objects initWithCoder的消息进行初始化。 在OS X环境下,包括views,cells,menus和view controller都是被发送initWithCoder消息进行初始化。
  • Custom views 则是被initWithFrame初始化。customs views是NSView的子类,并且xcode中没有代码实现。
  • 除了上述的custom views都是被发送init消息初始化。
  1. 重新建立所有的connection,包括nib file中的对象之间的actions, outlets and bindings。这包括与File's Owner以及其它placeholder object建立联系。建立这些connection的方法根据平台不同而不同。
  • Outlet connections 在OS X环境下,nib-loading首先试图使用对象的自己的method重新建立outlet connection,对于每一个outlet,CoCoa寻找形如setOutletName:的方法,如果存在,则调用它。如果不能找到,则CoCoa搜索对象中的实例变量,如果实例变量有相匹配的outlet name,则尝试直接设置该实例变量的值为该对象。如果没有找到,则connection就不会建立。 设置了outlet了一般为所有register observer会建立一个key-value observing(KVO) notification。这些通知可能在所有对象间连接重新建立之前发生,并且肯定在对象的任何awakeFromNib方法被调用之前发生。

  • Action connections 在OS X中,nib-loading code使用soucre object‘s setTarget:setAction: 方法和target object建立连接。如果这个target object不响应action method,则不会建立连接。如果target object是nil,action 被responder chain响应。

  • Bindings In OS X环境下,CoCoa使用source object的bind:toObject:withKeyPath:options:去建立它和它的target object之间的联系。

  1. send awakeFromNib message 在OS X环境下,这个message发送给所有定义了该方法的interface object。也发送给File’s Owner以及其它placeholder object。不能保证nib-loading代码调用awakeFromNib的顺序。Cocoa尝试最后调用File’s Owner的awakeFromNib方法,但是不能保证该行为。所以当你需要读写nib文件中的对象,那么最好在load-nib call调用返回之后。这个时候所有的对象都已经被创建,初始化和可以被安全访问。

  2. 最后nib文件中的启动后可见的选项为true的内容均可以展示出来。

6. 管理Xib Files中的对象的生命周期

当你每一次使用NSBundle或NSNib类去load xib file。那么会默认复制xib file中的对象(每一次load xib file都不会去重新重新利用前一次load所创建的对象)。也就是说,当你load xib file后,你需要管理这些对象的生命周期,当你不需要时,当你不需要时这些对象能够被正确回收。你需要一个strong reference引用top-level object,不是top-level object的可以使用weak reference,因为它们被它们的的parents持有。strong outlet一般来说指向framework classes。比如NSViewController's view outlet 和 NSWindowController‘s window outlet。下面用一个例子来说明。 在myViewController.xib文件中,我们创建了custom view,其中里面有一个NSTextField对象和NSImageView对象。分别建立outlet(在myViewController.m文件中),可以看出mainView outlet使用了strong,而textField outlet 使用了weak。因为mainView相当于top-level object,而textField属于它的child,所以可以使用weak。当mainView对象没有被销毁时,textField对象就不会被销毁,所以使用weak能够保证它的生命周期,且能够减少循环引用的错误发生。 截屏2022-01-02 14.02.33.png

截屏2022-01-02 14.03.33.png 有一种情况需要注意,当你的object graph中的subview需要被remove时,那么它的outelet应该使用strong修饰,因为此时它的生命周期需要单独管理。

分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改