搜集网上资源加以整理,方便温故知新!!!
iOS沙盒有哪些目录?
NSString *path = NSHomeDirectory();
上面的代码得到的是应用程序目录的路径,在该目录下有三个文件夹:Documents、Library、temp以及一个.app包!该目录下就是应用程序的沙盒,应用程序只能访问该目录下的文件夹!!!
1、Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes共享文件。可被iTunes备份。
2、AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
3、Library 目录:这个目录下有两个子目录:
Preferences 目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。
4、tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。
二、获取各种文件目录的路径
获取目录路径的方法:
// 获取沙盒主目录路径
NSString *homeDir = NSHomeDirectory();
// 获取Documents目录路径
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; // 获取Library的目录路径 NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; // 获取Caches目录路径 NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; // 获取tmp目录路径 NSString *tmpDir = NSTemporaryDirectory();
获取应用程序程序包中资源文件路径的方法:
NSLog(@"%@",[[NSBundle mainBundle] bundlePath]);
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"png"]; UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
设计模式
目前常用的几种设计模式:代理模式、观察者模式、MVC模式、单例模式、策略模式、工厂模式、MVVM
@autoreleasepool是什么?什么时候用?什么时候释放?
App启动后,系统在主线程RunLoop 里注册两个Observser,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件
是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件
_BeforeWaiting(准备进入休眠) 时 _
调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
_Exit(即将退出Loop) 时 _
调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。
现在我们知道了AutoreleasePool是在RunLoop即将进入RunLoop和准备进入休眠这两种状态的时候被创建和销毁的。
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"abc",nil];
NSMutableArray *mArrayCount = [NSMutableArray arrayWithCapacity:1];
[mArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock: ^(id obj,NSUInteger idx, BOOL *stop){
[mArrayCount addObject:[NSNumber numberWithInt:[obj length]]];
}];
NSLog(@"%@",mArrayCount);
---------------------
作者:brhave
来源:CSDN
原文:https://blog.csdn.net/bravegogo/article/details/50792437
版权声明:本文为博主原创文章,转载请附上博
什么是预编译,何时需要预编译
UIView 的init 方法和initWithFrame 方法的问题
//假如有一个View 代码如下:
@implementation MyView
- (id)init {
self = [super init];
if (self) {
NSLog(@" init");
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSLog(@"initWithFrame");
}
return self;
}
MyView *myView =[ [MyView alloc] init];
结果会输出什么?
再如果我们这样用
MyView *myView =[ [MyView alloc] initWithFrame:CGRectZero];
结果又会输出什么?
我们先看第一种输出结果:
第二种输出结果:
为什么会这样子呢?它们二者又有什么关系呢?
我们知道 Objective-C 有指定初始化方法(designated initializer)和间接(secondary initializer)初始化方法的观念。 designated 初始化方法是提供所有的参数,secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated 初始化的初始化方法。如果你对这两个方法不熟悉( 这本书里说的很详细也很好,在这里我就不复制了,如果你有兴趣了解更多那就下载下来好好看看吧) 请参考这篇文章 https://github.com/oa414/objc-zen-book-cn/#designated-initializer 这里面有详细说明。请仔细阅读理解,如果认真看完了那么你点开UIView.h 文件看看你可以看到:
- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
再回头看看上面的问题你是不是理解了呢?
UIViewController的生命周期
答:
#pragma mark --- life circle
// 非storyBoard(xib或非xib)都走这个方法
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
NSLog(@"%s", __FUNCTION__);
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
}
return self;
}
// 如果连接了串联图storyBoard 走这个方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
NSLog(@"%s", __FUNCTION__);
if (self = [super initWithCoder:aDecoder]) {
}
return self;
}
// xib 加载 完成
- (void)awakeFromNib {
[super awakeFromNib];
NSLog(@"%s", __FUNCTION__);
}
// 加载视图(默认从nib)
- (void)loadView {
NSLog(@"%s", __FUNCTION__);
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view.backgroundColor = [UIColor redColor];
}
//视图控制器中的视图加载完成,viewController自带的view加载完成
- (void)viewDidLoad {
NSLog(@"%s", __FUNCTION__);
[super viewDidLoad];
}
//视图将要出现
- (void)viewWillAppear:(BOOL)animated {
NSLog(@"%s", __FUNCTION__);
[super viewWillAppear:animated];
}
// view 即将布局其 Subviews
- (void)viewWillLayoutSubviews {
NSLog(@"%s", __FUNCTION__);
[super viewWillLayoutSubviews];
}
// view 已经布局其 Subviews
- (void)viewDidLayoutSubviews {
NSLog(@"%s", __FUNCTION__);
[super viewDidLayoutSubviews];
}
//视图已经出现
- (void)viewDidAppear:(BOOL)animated {
NSLog(@"%s", __FUNCTION__);
[super viewDidAppear:animated];
}
//视图将要消失
- (void)viewWillDisappear:(BOOL)animated {
NSLog(@"%s", __FUNCTION__);
[super viewWillDisappear:animated];
}
//视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
NSLog(@"%s", __FUNCTION__);
[super viewDidDisappear:animated];
}
//出现内存警告 //模拟内存警告:点击模拟器->hardware-> Simulate Memory Warning
- (void)didReceiveMemoryWarning {
NSLog(@"%s", __FUNCTION__);
[super didReceiveMemoryWarning];
}
// 视图被销毁
- (void)dealloc {
NSLog(@"%s", __FUNCTION__);
}
//打印结果
2017-03-01 18:03:41.577 ViewControllerLifeCircle[32254:401790] -[ViewController initWithCoder:]
2017-03-01 18:03:41.579 ViewControllerLifeCircle[32254:401790] -[ViewController awakeFromNib]
2017-03-01 18:03:41.581 ViewControllerLifeCircle[32254:401790] -[ViewController loadView]
2017-03-01 18:03:46.485 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLoad]
2017-03-01 18:03:46.486 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillAppear:]
2017-03-01 18:03:46.487 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillLayoutSubviews]
2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLayoutSubviews]
2017-03-01 18:03:46.490 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidAppear:]
2017-03-01 19:03:13.308 ViewControllerLifeCircle[32611:427962] -[ViewController viewWillDisappear:]
2017-03-01 19:03:14.683 ViewControllerLifeCircle[32611:427962] -[ViewController viewDidDisappear:]
2017-03-01 19:03:14.683 ViewControllerLifeCircle[32611:427962] -[ViewController dealloc]
2017-03-01 19:12:05.927 ViewControllerLifeCircle[32611:427962] -[ViewController didReceiveMemoryWarning]
Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
答:
Object-c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
#import 跟#include 又什么区别,@class呢, #import<> 跟 #import””又什么区别?
答:
#import是Objective-C导入头文件的关键字,
#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入,相当于#include和#pragma once;
@class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含;
#import<>用来包含系统的头文件,#import””用来包含用户头文件。
属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
答:
readwrite 是可读可写特性;需要生成getter方法和setter方法时
readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变
assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时。
nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic
对于语句NSString*obj = [[NSData alloc] init]; obj在编译时和运行时分别时什么类型的对象?
答:
编译时是NSString的类型;运行时是NSData类型的对象
常见的object-c的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int
答:
object-c的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,这些都是class,创建后便是对象,而C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值;NSInteger是基本数据类型,并不是NSNumber的子类,当然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是Long。
id 声明的对象有什么特性?
答:
Id 声明的对象具有运行时的特性,即可以指向任意类型的objcetive-c的对象;
原子(atomic)跟非原子(non-atomic)属性有什么区别?
答:
atomic提供多线程安全。是防止在写未完成的时候被另外一个线程读取,造成数据错误
non-atomic:在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。
Object-C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
答:
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
类别的作用?继承和类别在实现中有何区别?
答:
category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改,并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
类别主要有3个作用:
1).将类的实现分散到多个不同文件或多个不同框架中。
2).创建对私有方法的前向引用。
3).向对象添加非正式协议。
继承可以增加,修改或者删除方法,并且可以增加属性。
oc中的协议和java中的接口概念有何不同?
答:
网上大家都将oc中的interface理解为“非正式协议(或说接口)”,protocol理解为“正式协议(或说接口)”,我觉得那样理解起来虽然不困难,但是很变扭,于是我做一点自己的理解:
1、protocol就相当于java中的interface;
2、而interface和implementation共同代表一个类,两者的组合相当于java中的class,即oc中的类必须包括两部分,interface部分和implementation部分,这才是oc中的一个类的完整声明;然后OC中将成员变量和成员方法的声明部分放置在interface部分中,包括继承关系,protocol实现关系,都在interface里面的头部进行声明,然后将实现部分放置在implementation部分中,相当于是将类拆分成声明和实现两部分,这两部分缺一不可,所以在OC中,不妨不要将interface叫做接口,直接叫做类声明部分来得容易理解多了,简而言之,oc中interface是类的一个部分,和implementation共同组成一个完整的类。
iOS开发在@interface,@implementation和@property中变量的定义
//ViewController.h
@interface ViewController : UIViewController {
NSInteger a;
}
@property (nonatomic,assign) NSInteger b;
@end
//ViewController.m
@interface ViewController () {
NSInteger c;
}
@property (nonatomic,assign) NSInteger d;
@end
a,b,c,d这四个变量有什么不同?
答:
a是成员变量,相当于java中的protected,可以被子类继承。
b相当于java中的public,既可以被子类继承,也可以被外部访问。
c和d个人理解都是私有变量,相当于java中的private,不可以被子类调用,也不可以外部访问。
b 和 d 可以用 self.来访问, a和c不能。
a和c基本上很少用,一般都用b和d,如果需要对外开放的属性的就放在b,不需要的话直接在d位置定义。
a和c我觉的应该是为了兼容早期版本的写法,为了让你自己定义属性对应的内部变量,但是在arc之后的版本中,你只要定义一个属相,xcode会自动帮你定义一个以下划线打头的和属性同名的内部变量,所以你不需要另外再定义了
我们说的oc是动态运行时语言是什么意思?
答:
多态。 主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
运行时:简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
关于多态性
答:
多态,子类指针可以赋值给父类。
这个题目其实可以出到一切面向对象语言中,
因此关于多态,继承和封装基本最好都有个自我意识的理解,也并非一定要把书上资料上写的能背出来
frame和bounds有什么不同?
答:
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)
demo演示:
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 280, 250)];
[view1 setBounds:CGRectMake(-20, -20, 280, 250)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];//添加到self.view
NSLog(@"view1 frame:%@========view1 bounds:%@",NSStringFromCGRect(view1.frame),NSStringFromCGRect(view1.bounds));
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
view2.backgroundColor = [UIColor yellowColor];
[view1 addSubview:view2];//添加到view1上,[此时view1坐标系左上角起点为(-20,-20)]
NSLog(@"view2 frame:%@========view2 bounds:%@",NSStringFromCGRect(view2.frame),NSStringFromCGRect(view2.bounds));
注意: //setBounds 强制将自己坐标系的左上角点改为(-20,-20)。那么真正的原点(0,0)自然向右下角偏移(20,20);
运行结果:
方法和选择器有何不同?
答:
selector是一个方法的名字,method是一个组合体,包含了名字和实现.
OC的垃圾回收机制?
答:
与Java语言相同Objective-c 2.0之后,也提供了垃圾回收机制。但是在iOS移动终端设备中,并不支持垃圾回收机制。因此,iPhone并不能对内存进行自动垃圾回收处理(autorelease)。因此需要注意垃圾回收机制并不是ARC,ARC也是需要管理内存的,只不过是隐式的管理内存,编译器会再适当的地方自动插入retain,release和autorelease消息。ARC是在IOS5之后推出的新技术,它与GC的机制是不同的。
在应用中可以创建多少autorelease对象,是否有限制?
答:
无
类和结构体区别?【重要!!!】
答:
1、结构体只能封装属性,类却不仅可以封装属性也可以封装方法。如果一个封装的数据有属性也有行为,就只能用类了。
2、结构体变量分配在栈,而OC对象分配在堆,栈的空间相对于堆来说是比较小的,但是存储在栈中的数据访问效率相对于堆而言是比较高
3、堆的存储空间比较大,存储在堆中的数据访问效率相对于栈而言是比较低的
4、如果定义一个结构体,这个结构体中有很多属性,那么这个时候结构体变量在栈中会占据很多空间,这样的话就会降低效率
5、我们使用结构体的时候最好是属性比较少的结构体对象如果属性较多的话就要使用类了。
6、结构体赋值的话是直接赋值,对象的指针,赋值的是对象的地址。
类NSObject的那些方法经常被使用?
答:
NSObject是Objetive-C的基类,其由NSObject类及一系列协议构成
其中类方法alloc、class、 description 对象方法init、dealloc、– performSelector:withObject:afterDelay:等经常被使用
什么是简便构造方法?
答:
简便构造方法一般由CocoaTouch框架提供,如NSNumber的 + numberWithBool: + numberWithChar: + numberWithDouble: + numberWithFloat: + numberWithInt:
Foundation下大部分类均有简便构造方法,我们可以通过简便构造方法,获得系统给我们创建好的对象,并且不需要手动释放。
C和obj-c 如何混用
答:
1).obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp
2).在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是问题
3).在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的。
如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。
如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。
总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用实现代码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp
Objective-C堆和栈的区别?【重要!!!】
答:
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是彼此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:
堆都是动态分配的,没有静态分配的堆。
栈有2种分配方式:静态分配和动态分配。
静态分配是编译器完成的,比如局部变量的分配。
动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需 我们手工实现。
分配效率:
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆则是C/C++函数库提供的,它的机制是很复杂的。
用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
答:
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在这想看到几件事情:
#define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。
重要:
上边这种写法其实不正确的!!!
写一个”标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
答:
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比 if-then-else 更优化的代码,了解这个用法是很重要的。
懂得在宏中小心地把参数用括号括起来
我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
结果是:
((*p++) <= (b) ? (*p++) : (*p++))
这个表达式会产生副作用,指针p会作三次++自增操作。
- 开发界面所见即所得,可以快速通过拖拽构造界面。
- 你可以从 storyboard 中很方便地梳理出所有View Controller的界面间的调用关系。这一点对于新加入项目组的开发同事来说,比较友好。
- 使用 Storyboard 可以使用Table View Controller的 Static Cell 功能。对于开发一些 Cell 不多,但每个 Cell 都不一样的列表类设置界面会比较方便。
- 通过实现 – (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 方法,每个 View Controller 的跳转逻辑都聚集在一处,这方便我们统一管理界面跳转和传递数据。
- Storyboard 可以方便将一些常用功能模块化和复用。例如 WWDC2011 年介绍 Storyboard 的视频就将微博分享功能模块化成一个单独的 Storyboard。
- xib 对版本管理是灾难。storyboard 实际上的多个 xib 的集合,所以更容易让多人编辑产生冲突。而虽然它们是 xml 格式,但是冲突解决起来还是不如代码那么容易。
- 苹果对 xib, storyboard 的设计中带有当前电脑的操作系统版本和 Xcode 版本。所以如果两个协作的开发者电脑操作系统或 Xcode 有不一样的话,每次打开必定会修改这个文件。另外即使操作系统版本和 Xcode 版本一样,有些时候打开看也会造成一些自动的修改。
- storyboard 带来的 segue 的概念对于开发来说并不省事,特别是在需要传递参数的时候。如果是用程序内部 trigger 一个 segue,那么需要在另一个回调的地方设置 dest view controller 的参数信息。
- 我们发现 xib 中设置的颜色值并不精确,RGB 在真机 / 模拟器上常常会有 10 多像素的偏差。
- xib 和 storyboard 对继承的支持并不友好。无法做界面的继承。
- xib 和 storyboard 对搜索支持并不友好,无法方便地在 Xcode 中查找关键词(但是可以通过写 bash 命令来查找)。
- storyboard 对组合支持得不太好,不允许在一个 xib 中附带多个子 view。
- xib 和 storyboard 不太方便做界面的模块化管理,比如我们想统一修改界面中所有按钮的字体样式,那么在 xib 和 storyboard 只能一个一个手工修改,而如果是代码编写的,则只需要改一个工厂方法的实现即可。
- 对于复杂的 App,storyboard 的性能会比较差。
IB全称:Interface Builder,从 Xcode4 开始,Interface Builder 已经整合到Xcode 中。