选择题
1、在Xcode中,需要编译混合Objective-C和C++的源码文件,需要将文件格式的后缀改为 (C)
A. .c
B. .cpp
C. .mm
D. .m
2、下面的http状态码中哪一个是状态码是标注请求不正确地:(A)
A.302 是请求链接错误或者找不到服务器。
B.500以上是服务器错误。
C.200以上是正确。
D.100以上是请求接受成功。
3、下面关于Objective-C内存管理的描述错误的是 (A)
A. 当使用ARC来管理内存时,代码中可以出现autorelease
B. autoreleasepool 在 drain 的时候会释放在其中分配的对象
C. 当使用ARC来管理内存时,在线程中大量分配对象而不用autoreleasepool则可 能会造成内存泄露
在一个garbage collected环境里,release不做任何操作。 NSAutoreleasePool因此提供了一个 drain 方法,它在reference-counted环境中的行为和调用release一样, 但是在一个garbage collected环境中则触发garbage collection动作 (if the memory allocated since the last collection is greater than the current threshold)。 因此通常你应该使用drain而不是release去释放一个autorelease pool
4、使用imageNamed方法创建UIImage对象时,与普通的init方法有什么区别?(C)
A. 没有区别,只是为了方便
B. imageNamed方法只是创建了一个指针,没有分配其他内存
C. imageNamed方法将图片加载到内存中后不再释放
D. imageNamed方法将使用完图片后立即释放
5、下面对category描述不正确的是(B)
A.category可以添加新的方法
B.category可以删除修改之前的方法
C.将类的实现分散到多个不同文件或多个不同框架中
D.创建对私有方法的前向引用
6、多线程中栈与堆是公有的还是私有的(C)
A.栈公有,堆私有
B.栈公有,堆公有
C.栈私有,堆公有
D.栈私有,堆私有
解释:
一般来说栈是私有的,堆是公有的;但是可以为特定的线程创建私有的堆
在多线程环境下,每个线程拥有一个栈和一个程序计数器。栈和程序计数器用来保存线程的执行历史和线程的执行状态,是线程私有的资源。其他的资源(比如堆、地址空间、全局变量)是由同一个进程内的多个线程共享。
堆: 是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是 thread safe的。操作系统在切换线程的时候会自动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
7、单例类NSNotificationCenter提供信息广播通知,它采用的是观察者模式的通知机制。(A)
A.对
B.错
8、判断是否为iPad设备语句是?(C)
A.if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {...}
B.if ([[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPhone) {...}
C.if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {…}
9、以下哪个算法不是对称加密算法(D)
A.DES
B.RC5
C.AES
D.RSA
解释:
对称加密:(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。
DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是56位,其算法主要分为两步:
1)初始置换
其功能是把输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位,其置换规则为将输入的第58位换到第一位,第50位换到第2位……依此类推,最后一位是原来的第7位。
其置换规则见下表:
58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,
62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,
57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,
61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7,
2)逆置换
经过16次迭代运算后,得到L16、R16,将此作为输入,进行逆置换,逆置换正好是初始置换的逆运算,由此即得到密文输出。
此算法是对称加密算法体系中的代表,在计算机网络系统中广泛使用.
AES:高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
10、已知有序序列b c d e f g q r s t,则在二分查找关键字b的过程中,先后进行比较的关键字依次是多少?(B)
A.f d b
B.f c b
C.g c b
D.g d b
解释:
使用折半查找的序列必须是有序的(要么从小到大,要么从大到小)。
本题显然是一个从小到大的一个序列。
二分查找的重点在于记录查找区间。
第一次查找区间是整个区间[0,9];也就是从0元素到9元素。
记录
f = 0;
l = 9;
那他们的中间位置就是
m = (f + l) / 2 = 4;(舍去小数点)
4元素的值是f。我们要查找的值视b显然不是,但是我们发现b比f小,由于序列是有序的,那么我们要找的b就在f的左边(不包含f)。
重新修改查找区间[0,3];
记录
f = 0;
l = 3;
m = (f + l) / 2 = 1;
1元素的值c。依然不是。b还是小于c。
再调整查找区间[0,0];
f = 0;
l = 0;
m = (f + l ) / 2 = 0;
0元素就是b。找到。
所以依次比较f c b
11、使用Xcode创建工程时,支持同时创建的版本管理库是(C)
A. Subversion
B. Mercurial
C. Git
D. Concurrent Versions System
12、UIEdgeInsets edgeInsets = UIEdgeInsetsMake(10, 10, 10, 10);
@synchronized(edgeInsets) {
}关于以上代码,正确的说法:(D)
A. 写在大括号内部的代码,会保证多线程访问edgeInsets不出错
B. UIEdgeInsetsMake是构造UIEdgeInsets数据类型的方法
C. UIEdgeInsets本质上是OC中的基本数据类型
D. 代码错误
解释:
只能存放对象类型 id 现在是一个基本数据类型
UIEdgeInsets 是比较特殊的类型,和NSRange 、CGRect一样本质上都是结构体类型。因为OC没有命名空间,所以只能使用前缀来区分,但OC中许多非对象类型同样使用和对象类型的一样前缀,注意甄别(一般非对象类型创建时无*号,id除外)。
@synchronized用来保护被访问的对象同时只能有一个线程操作,保证了线程访问对象时不出错。
13、添加子视图的方法中,是下列哪个面向对象特性的体现?(C)
A. 封装
B. 继承
C. 多态
D. 协议
多态 : addsubview 父类指针指向子类对象 view类型 — label
14、 优秀的程序设计,应遵循下列哪个准则?(B)
A. 高内聚,高耦合
B. 高内聚,低耦合
C. 低内聚,高耦合
D. 低内聚,低耦合
15、为应用程序创建window的时候,以下哪个是声明成属性的必要原因(B)?
A. 类的内部可以访问。
B. 类的外部可以访问。
C. 延长生命周期。
D. 拷贝window副本。
16、以下那个属性可以导致view向右向下平移10个point?(D)
A. simpleView.transform = CGAffineTransformMake(10,10, 1, 1, 0, 0);
B. simpleView.transform = CGAffineTransformMake(1, 1, 10, 10, 0, 0);
C. simpleView.transform = CGAffineTransformMake(1, 0, 1, 0, 10, 10);
D. simpleView.transform = CGAffineTransformMake(1, 0, 0, 1, 10, 10);
解释:
17、以下是定义cell重用标识符的语句:static NSString * const reuseIdentifier = @"Cell";
关于以上代码说法正确的是?(C)
A. static是为了修饰变量是全局区域的
B. static是为了修饰变量是可以被其它类引用为全局变量的
C. const是为了修饰 reuseIdentifier指针本身不可以改变
D. const是为了修饰 reuseIdentifier指针指向的内容不可以改变
18、Block作为属性在ARC下应该使用的语义设置为?(D)
A. retain
B. weak
C. strong
D. copy
解释:
开发者使用 block 的时候苹果官方文档中说明推荐使用 copy,使用 copy 的原因就在于大家所熟知的把block从栈管理过渡到堆管理
在 ARC 下面苹果果帮助我们完成了 copy 的工作,在 ARC 下面即使使用的修饰符是 Strong,实际上效果和使用 copy 是一样的这一点在苹果的官方文档也有说明
19、下列哪项不属于数据持久化?(D)
A. 属性列表
B. Core Data
C. NSUserDefaults
D. 归档和反归档
20、下面哪个类可以创建可变对象(C)
A.NSString
B. NSArray
C. NSMutableDictionary
D.NSSet
简答题
1#import跟#include 有什么区别,@class呢, #import<> 跟 #import””有什么区别?
#import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使 用#import只导入一次,不会重复导入,相当于#include和#pragma once;
@class一般用于声明某个字符串作为类名使用,它只是声明了一个类名,没有导入.h文件中的内容,不会引起交叉编译的问题,在m文件中还是需要使用#import。
#import<>用来包含系统的头文件, #import””用来包含用户头文件。
2. OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?
1.创建线程的方法
NSThread
NSOperationQueue和NSOperation
GCD
主线程中执行代码
1:[self performSelectorOnMainThread: withObject: waitUntilDone:];
2:[self performSelector: onThread:[NSThread mainThread] withObject: waitUntilDone:];
3:dispatch_async(dispatch_get_main_queue(), ^{
});
3. 浅复制和深复制的区别?
用一句简单的话来说就是浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
copyWithZone和mutableCopyWithZone方法中的不同编程方式
http://segmentfault.com/a/1190000000604331
4. 类别和延展的区别?
类目:
1、类目里不能添加实例变量,但是类目声明可以用属性,其实属性就是一对儿方法,那么在.m里面需要实现这个属性的setter方法和getter方法,在这两个实现方法里面依然不能使用自己添加的实例变量。
2、类目里添加的方法不能和原始类中的方法重名,否则会导致覆盖。
3、一个类可以添加多个类目,但是类目名和方法名不能重复。
4、类目中的方法可以成为原始类的一部分,和原始类方法级别相同,可以被子类继承。
延展:
1、如果括号里没有类目名,则认为延展里面的方法为全都必须实现,如果有名,则认为是可选实现。
2、虽然延展是给一个类定义私有方法,但是没有OC没有绝对的私有方法,其实还是可以调用,另外延展里面声明的变量只能在该类内部使用,外界访问不了
5. 我们说的OC是动态运行时语言是什么意思?
主要是将编译时确定的数据类型,推迟到了运行时。 这个问题涉及到两个概念,运行时和多态。 简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。 多态:不同对象以自己的方式响应相同的消息的能力叫做多态。C、C++等语言则不属于动态语言。OC通过运行时实现了动态类型特征,使它具备了多态特性。
6. 类变量的@protected ,@private,@public,@package,声明各有什么含义?
@private作用范围只能在自身类(外界既不可访问,又不能继承);
@protected作用范围在自身类和子类,如果什么都不加修饰,默认是@protected(外界不可访问,但是可以继承);
@public作用范围最大,可以在任何地方被访问(外界即可访问,又可以继承);
@package作用范围在某个框架内
7. 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与alloc配对使用的方法是dealloc还是release,为什么?readwrite、readonly、assign、retain、copy、nonatomic、atomic、strong、weak属性的作用?
A、OC中的内存管理机制有三种,分别是:1、垃圾回收机制(GC):系统在分配内存空间时,将已被释放的内存回收并重新分配,这种机制对内存的管理不够及时,在OS X 10.6之后被废弃;2、手动引用计数管理机制(MRC):由程序员负责开辟内存空间,并管理内存空间的引用计数,每引用一次就将计数器加一,不再引用时将计数器减一,当计数器为零时,马上回收这块内存空间,这种机制能做到对内存的精确控制,不使用的内存能及时的回收,但是对程序员的要求较高;自动引用计数机制(ARC):由程序员开辟内存空间,由编译器帮我们在合适的地方添加引用计数操作的代码,不需要程序员手动管理内存的引用计数,使用这种机制即可以精确的控制内存的分配与回收,还可以让程序员专心于程序的编写,而不是内存计数器的控制。ARC是以MRC为基础的一种机制,ARC并不是GC。iOS平台中,由于内存比珍贵,所以使用手动引用计数管理机制或自动引用技术管理机制。
B、与retain配对使用的方法是release,因为retain是将内存的引用计数加一(对对象进行一次持有),release是将内存的引用计数减一(结束对对象的持有)。
C、与alloc对应的方法是dealloc,因为alloc表示开辟内存空间,创建对象,dealloc表示回收内存空间,释放对象。
D、readwrite表示读写设置中的可读可写(读写设置的默认值),编译器会为读写设置声明为”readwrite”的属性自动合成setter和getter方法。
E、readonly表示读写设置中的只读,编译器会为读写设置声明为”readonly“的属性自动合成”getter”方法,而不合成”setter“方法。
F、assign是语义设置(语义设置的默认值),使用assign修饰的属性所对应的读写方法中不会操作内存的引用计数,所以一般将不需要程序员管理的数据类型声明为assign,如基本数据类型和结构体类型。
G、retain是语义设置,使用retain修饰的属性所对应的读写方法中会对内存的引用计数进行操作,以保证持有的对象不会被意外回收,OC中的对象使用retain修饰。
H、copy是语义设置,使用copy修饰的属性所对应的读写方法中会将将要持有的对象拷贝一份,并持有对象的副本而不是原对象。对于OC中的可变对象,使用copy修饰,是”深拷贝“,在内存中生成一块新的内存空间,将原对象拷贝到新空间中;对于OC中的不可变对象,使用copy修饰,是”浅拷贝“,只拷贝对象的内存地址。
I、atomic:是原子特性(原子设置的默认值),在多线程环境下,使用atomic会保证在同一时刻只有一条线程修改、访问对应的实例变量,保证了实例变量在多线程环境下的安全性,但是比较耗费资源,所以只有对于需要线程安全的属性才使用atomic来修饰。
J、nonatomic:是原子特性,在多线程环境下不能保证线程安全,这种设置不会消耗过多的资源,所以不需要保证多线程环境中线程安全的属性,声明为nonatomic。
K、strong:ARC中的语义设置,相当于MRC中的retain,会操作内存的引用计数。
L、week:ARC中的语义设置,相当于MRC中的assign,不仅不会操作内存的引用计数,而且在对象被回收后,会将指针置为nil,防止”野指针“的出现。
8. ViewController的alloc,loadView。viewDidLoad,viewWillAppear,dealloc、init分别是在什么时候调用的?在自定义ViewController的时候这几个函数里面应该做什么工作?
alloc:创建对象,分配内存空间时调用;init:初始化对象,初始化数据调用;loadViwe:从nib载入视图 ,通常这一步不需要去干涉。除非你没有使用xib文件创建视图;viewDidLoad:载入完成,可以进行自定义数据以及动态创建其他控件;viewWillAppear:视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了;
9. 线程是什么?进程是什么?二者有什么区别和联系?
进程:一个程序对一个数据集的动态执行过程,是分配资源的基本单位。
线程:一个进程内的基本调度单位。
线程的划分尺度小于进程,一个进程包含一个或者更多的线程。
10. RunLoop是什么?
Runloop是线程的基础架构部分。一个Runloop就是一个事件处理循环,用来不停的调配工作以及处理输入事件。使用Runloop的目的是使你的线程在有工作的时候工作,没有的时候休眠。
Runloop的管理并不完全是自动的。你仍必须设计你的线程代码以在适当的时候启动Runloop并正确响应输入事件。Cocoa和CoreFundation都提供了Runloop对象方便配置和管理线程的Runloop。你创建的程序不需要显示的创建Runloop;每个线程,包括程序的主线程(main thread)都有与之相应的Runloop对象。但是,自己创建的次线程是需要手动运行Runloop的。在carbon和cocoa程序中,程序启动时,主线程会自行创建并运行Runloop。
11. 为什么很多内置类如UITableViewController的delegate是assign?
为了防止因为互相持有导致的不能被释放问题。使用retain修饰delegate会使delegate的引用计数加一,如果互为代理,则会导致双方互相持有,最终任何一方都不能被释放。而使用assign不会影响计数器,在使用结束后能被及时释放
12. What is lazy loading?
它被称为懒加载,因为像一个懒惰的人,你推迟做事你直到必要的时候。若应用需要加载的内容多,启动时就要加载很多内容。那么这个应用程序需要很长的时间来启动。
相比,延迟加载,则应用程序将启动快多了,因此,使用延迟加载,用来分摊加载时间到整个应用程序运行的过程中。
13. NSAutoreleasePool 是怎么工作的?
自动释放池以栈的形式实现:当你创建一个新的自动释放池时,它将被添加到栈顶。当一个对象收到发送autorelease消息时,它被添加到当前线程的处于栈顶的自动释放池中,当自动释放池被回收时,它们从栈中被删除, 并且会给池子里面所有的对象都会做一次release操作
一般来说在应用的main thread中, 已经存在了一个autorelease pool. 有两种情况需要开发者自己新建autorelease pool:
1.在main thread中, 在某个方法中出现大量的autoreleased objects, 为了避免memory footprint的增大, 可以手动创建一些autorelease pool用来drain objects.
2.创建新的thread, 并在其中访问了Cocoa, 需要在访问的前创建autorelease pool, 访问结束后drain.
最后一点, 在每个thread中都会维持一个stack, 其中放置着所有在这个thread中创建但未销毁的pool, 每当一个新的pool创建后, 它就位于stack的最顶端, 相应autoreleased object就会放入其中. 当pool drain的时候, 它就会从stack的顶端移除, 并且release掉其包含的objects..
14. 描述一下复用机制
复用机制的目的:效率,内存。
查看UITableView头文件,会找到NSMutableArray* visiableCells,和NSMutableDictnery* reusableTableCells两个结构。visiableCells内保存当前显示的cells,reusableTableCells保存可重用的cells。
TableView显示之初,reusableTableCells为空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。开始的cell都是通过[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建。
比如:有100条数据,iPhone一屏最多显示10个cell。程序最开始显示TableView的情况是:
1. 用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]创建10次cell,并给cell指定同样的重用标识(当然,可以为不同显示类型的cell指定不同的标识)。并且10个cell全部都加入到visiableCells数组,reusableTableCells为空。
2. 向下拖动tableView,当cell1完全移出屏幕,并且cell11(它也是alloc出来的,原因同上)完全显示出来的时候。cell11加入到visiableCells,cell1移出visiableCells,cell1加入到reusableTableCells。
3. 接着向下拖动tableView,因为reusableTableCells中已经有值,所以,当需要显示新的cell,cellForRowAtIndexPath再次被调用的时候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1移出reusableTableCells;cell2移出visiableCells,cell2加入到reusableTableCells。之后再需要显示的Cell就可以正常重用了。
15. POST 和 GET 有何区别
两种请求⽅方式⽐比较
不同点:
1 给服务器传输数据的方式:
GET:通过网址字符串。
POST:通过data
2、传输数据的⼤小:
GET:⽹址字符串最多255字节。
POST:使⽤NSData,容量超过1G
3、安全性:
GET:所有传输给服务器的数据,显⽰示在网址里,类似于密码的明文输入,直接可见。
POST:数据被转成NSData(二进制数据),类似于密码的密⽂文输⼊入,⽆法直接读取
16. 简述可视化和纯代码的优缺点。
可视化优点:
1.节省代码和时间;
2.能够直观地看到界面布局效果;
3.可以明确地看到各个控制器之间的转换关系(storyBoard)
4.更加方便轻松地进行页面适配。
5.xib设计之初的目的之一是更好地实现MVC,xib的内容大多都是view,有助于更好地实现MVC模式;
6.一些功能难以实现。
缺点:
1.storyBoard文件一般过大,会导致加载过慢;
2.团队开发时候容易导致合并冲突;
3.代码修改会覆盖xib、storyBoard里面的操作,导致出现bug不易查找。
4.重用性较代码编程较低。
纯代码优点:
1.可操控性高,能轻松地修改、添加;
2.重用性高,方便封装;
3.易于维护和解决bug;
4.方便团队开发,冲突容易查找解决;
5.有一些xib和storyBoard难以实现的功能,代码可以实现;
缺点:
1.代码适配相对于可视化难度大;
2.需要不断地编译运行才能看到UI效果;
3.纯代码布局所费时间大部分时候较长,且代码冗余较多。
17. 有没有在AppStore上架产品?简单描述上架过程。
1、创建 APP 对应的(Distribution Certification)发布证书,Distribution Provisioning Profile(发布授权文件),ID。生成Distribution Provisioning Profile(发布授权文件)并下载。
2、登录itunesconnect.apple.com,创建应用,填写应用相关信息。
3、打开 xcode 进行上传应用。release应用,并通过product-archive或application loader上传应用。
18. 数据持久化存储方案有哪些?
1)文件读写,例如:plist文件(属性列表)
2)NSUserDefaults(偏好设置)
3) NSKeyedArchiver(归档)
4)SQLite
5) CoreData
19. 沙盒的目录结构是怎样的?各自一般用于什么场合?
iOS程序默认情况下只能访问程序自己的目录,这个目录被称为“沙盒”。
"应用程序包"
Documents
Library
Caches
Preferences
Tmp
应用程序包:
NSString *path = [[NSBundle mainBundle] bundlePath];
Documents: 最常用的目录,iTunes同步该应用时会同步此文件夹中的内容,适合存储重要数据。
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
Library/Caches: iTunes不会同步此文件夹,适合存储体积大,不需要备份的非重要数据。
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
Library/Preferences: iTunes同步该应用时会同步此文件夹中的内容,通常保存应用的设置信息。
tmp: iTunes不会同步此文件夹,系统可能在应用没运行时就删除该目录下的文件,所以此目录适合保存应用中的一些临时文件,用完就删除。
NSString *path = NSTemporaryDirectory();
20. 介绍一下响应者链以及它的工作流程。
在iOS中,由响应者链来对事件进行响应,所有事件响应的类都是UIResponder的子类,响应者链是一个由不同对象组成的层次结构,其中的每个对象将依次获得响应事件消息的机会。当发生事件时,事件首先被发送给第一响应者,第一响应者往往是事件发生的视图,也就是用户触摸屏幕的地方。事件将沿着响应者链一直向下传递,直到被接受并做出处理。一般来说,第一响应者是个视图对象或者其子类对象,当其被触摸后事件被交由它处理,如果它不处理,事件就会被传递给它的视图控制器对象viewcontroller(如果存在),然后是它的父视图(superview)对象(如果存在),以此类推,直到顶层视图。接下来会沿着顶层视图(top view)到窗口(UIWindow对象)再到程序(UIApplication对象)。如果整个过程都没有响应这个事件,该事件就被丢弃。一般情况下,在响应者链中只要由对象处理事件,事件就停止传递。
一个典型的相应路线图如:
First Responser -- > The Window -- >The Application -- > App Delegate
正常的响应者链流程经常被委托(delegation)打断,一个对象(通常是视图)可能将响应工作委托给另一个对象来完成(通常是视图控制器ViewController),这就是为什么做事件响应时在ViewController中必须实现相应协议来实现事件委托。在iOS中,存在UIResponder类,它定义了响应者对象的所有方法。UIApplication、UIView等类都继承了UIResponder类,UIWindow和UIKit中的控件因为继承了UIView,所以也间接继承了UIResponder类,这些类的实例都可以当作响应者