iOS面试题二
选择题
1: A, B, C, D
image.png解答:
loadView:当控制器的根视图view为空,且此view被访问时调用。
viewDidLoad:loadView调用之后被调用。
viewWillApear:当控制器根视图view被添加到父视图上时调用。
viewDidUnload:iOS6.0之前,当内存警告时,先卸载视图,再调用viewDidUnload来释放内存。
2: B, C
image.png解答:
B.把声明和实现都放到
.h
文件中,链接的时候会报错。声明和实现都放到.h文件.png
错误.png
C.一个文件(Person.h Person.m)
可以定义一个多个类。
3. D
image.png解答:
因为
number
是实例变量,但是printNumber
是类方法,在类方法里面没办法直接使用实例变量。
image.png
image.png
4. D
image.png解答:
事件处理的整个流程:
-
触摸屏幕产生触摸事件后,触摸事件会被添加到
UIApplication
管理的事件队列中(首先收到事件的是UIApplication
); -
UIApplication
会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keywindow
); -
主窗口会在视图层次结构中找到一个最合适的
view
来处理该触摸事件。 -
最合适的
view
会调用自己的touches
相关方法处理事件,同时最合适的view
可以决定是否将事件顺着响应链上下抛通过nextResopnse
传递到下个响应者。
(备注:touches
默认做法是将事件顺着响应链向上抛)
这里点击一个button
后:
- 点击事件通过
UIApplication
传递给keywindow
,keywindow
在视图层次结构中找到最合适的view就是该button本身,来处理该点击事件。
-button
处理该点击事件,同时拒绝将该事件顺着响应链上下抛通过nextResopnse
传递到下个响应者,因此button就是响应链的最后一个响应者。
5: A
image.png解答:
dispatch_sync 表示一个同步线程
dispatch_get_main_queue 表示运行在主线程中的主队列,这是一个串行队列。
任务2是同步线程的任务。
-
首先,执行
任务1
,这是肯定没问题的,只是接下来,程序遇到了同步线程,那么它会进入等待,等待任务2
执行完,然后执行任务3
. -
但是这是串行队列,主队列有任务来,当然会将任务加到对尾,然后FIFO原则执行任务。因此
任务2
就会被加到主队列最后,任务3
排在任务2
前面,这样问题来了:
** 由于同步线程的阻塞,任务3
要等任务2
执行完才能执行,任务2
由于排在任务3
后面,意味着任务2
要在任务3
执行完后才能执行,所以他们进入了相互等待的局面。**
** 如图所示 : **
image详见:GCD 死锁 案例 分析
6. A
image.pngNSString *path = NSHomeDirectory();
是应用程序目录的路径,在该目录下有三个文件夹:Documents、Library、temp
以及一个.app
包!该目录下就是应用程序的沙盒,应用程序只能访问该目录下的文件夹!!!
-
AppName.app
目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。 -
Documents
目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes
共享文件。可被iTunes
备份。
只有用户生成的文件、其他数据及其他程序不能重新创建的文件,应该保存在/Documents
目录下面,并将通过iCloud
自动备份。
一般用来存放应用中建立的文件, 如数据库文件, 或者程序中浏览的数据, 如果进行备份将会备份此文件夹内容 -
Library
目录:这个目录下有两个子目录:-
Preferences
目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好. -
Caches
目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。可以重新下载或者重新生成的数据应该保存在/Library /caches目录下面。举个例子,比如杂志、新闻、地图应用使用的数据库缓存文件和可下载内容应该保存到这个文件夹。 -
可创建子文件夹,用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。
-
-
tmp
目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes
备份。尽管iCloud
不会备份这些文件,但在应用使用完这些数据之后要注意随时删除,避免占用用户设备的空间。
7: A. C
image.pngB.本地推送在iOS7
,不需要用户授权就可发出通知,而iOS8
以后,必须用户授权才可以发出通知
D.用户点击推送启动应用时,在application:didFinishLaunchingWithOptions:
方法中可以通过[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]
;
获得推送的userinfo
.
8. A
image.png解答:
loadView
-
loadView
方法是用来负责创建UIViewController
的view
-
在每次访问
UIViewController
的view
(比如controller.view、self.view
)而且view
为nil
,loadView
方法就会被调用。 -
默认调用
[super loadView]
,该函数会先去查找与UIViewController
相关联的xib
文件,通过加载xib
文件来创建UIViewController
的view
;如果没有明显地传xib
文件名,就会加载跟UIViewControlle
r同名的xib
文件;如果没有找到相关联的xib
文件,就会创建一个空白的UIView
,然后赋值给UIViewController
的view
属性. -
想要代码来创建
UIViewController
的view
,就要重写loadView
方法,并且不需要调用[super loadView]
,因为若没有xib
文件,[super loadView]
默认会创建一个空白的UIView
。我们既然要通过代码来自定义UIView
,那么就没必要事先创建一个空白的UIView
,以节省不必要的开销。正确的做法应该是这样:
9: C
image.png解答:
10: B
image.png解答:
self.var = arr;
会调用- (void)setVar:(NSArray *)arr
,方法,所以会导致循环调用。
简答题:
1.
image.png解答:
a:
typedef int (^FJTestBlock) (int a, int b);
FJTestBlock block = ^(int a, int b) {
return a + b;
};
block(4, 6);
b: block在内存管理上需要注意循环引用。
#import "FJTestViewController.h"
typedef int (^FJTestBlock) (int a, int b);
@interface FJTestViewController ()
@property (nonatomic, copy) FJTestBlock testBlock;
@end
@implementation FJTestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.testBlock = ^int(int a, int b) {
NSLog(@"%@", self);
return a + b;
};
}
像这样,FJTestViewController
持有testBlock
,testBlock
的Block
内部持有self
,就造成了循环引用,无法释放。
可以通过如下方式来避免循环引用:
__weak typeof(self) weakSelf = self;
self.testBlock = ^int(int a, int b) {
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
NSLog(@"%@", strongSelf);
}
return a + b;
};
- 在
block
之前定义对self
的一个弱引用weakSelf
因为是弱引用,所以self
被释放时weakSelf
会变为nil
; - 在
block
中引用该弱引用,考虑到多线程情况,通过强引用strongSelf
来引用该弱引用,这时如果self
不为nil
就会retain self
,以防止在block
内部使用过程中self
被释放。 - 在
block
块中使用该强引用strongSelf
,注意对strongSelf
进行nil
检测,因为多线程在弱引用weakSelf
对强引用strongSelf
赋值时,弱引用weakSelf
可能已经为nil
了 - 强引用
strongSelf
在block
作用域结束之后,自动释放。
2.
image.png解答:
//利用GCD并行多个线程并且等待所有线程结束之后再执行其它任务
dispatch_group_t tmpGroup = dispatch_group_create();
dispatch_group_async(tmpGroup, dispatch_get_global_queue(0, 0), ^{
// 并行执行的A
});
dispatch_group_async(tmpGroup, dispatch_get_global_queue(0, 0), ^{
// 并行执行的B
});
// A 、B 执行完成之后
dispatch_group_notify(tmpGroup, dispatch_get_global_queue(0, 0), ^{
// 并行执行的C
});
3.
image.png解答:
SEL
:类成员的方法指针,不同于C
中的函数指针,SEL
只是一个编号。IMP
: 函数指针,指向我们定义的函数
SEL
和IMP
的关系
任何继承NSObject
的类都会的得到runtime
的支持,在类中有一个isa
指针,指向该类定义的成员组成的结构体,这个结构体是编译时编译器为(NSObject)类
创建的,在这个结构体中包含一个指向父类的指针和一个Dispatch table(分发表)
,这个Dispatch table
指明了SEL
和 IMP
的对应关系。
4.
image.png解答:
#import "FJTestViewController.h"
BOOL globalFlag = NO;
@interface FJTestViewController ()
// 队列
@property (nonatomic, strong) dispatch_queue_t queue;
@end
@implementation FJTestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
for(NSInteger tmpIndex = 0; tmpIndex < 10; tmpIndex++) {
dispatch_async(self.queue, ^{
if (globalFlag) {
return ;
}
NSLog(@"%ld", (long)tmpIndex);
});
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
globalFlag = YES;
}
@end
通过添加全局变量globalFlag
来判断,默认设置globalFlag
为NO
,如果页面退出,将globalFlag
设置为YES
,然后在异步里面判断如果globalFlag
为YES
,就直接return
.
5.
image.png解答:
当执行该方法是会出现: 程序崩溃
。
原因:
-
在
dealloc
函数中会调用objc_clear_deallocating
方法,来清空引用计数表并清除弱引用表,将所有weak
引用指nil
。 -
__weak __typeof(self) weakSelf = self;·
该赋值操作,会调用objc_initWeak
函数,该函数会调用weak_register_no_lock
为新对新添加注册操作,weak_register_no_lock
内部会判断该对象是否在deallocating
,如果改对象处于deallocating
状态,就调用_objc_fatal
函数,最后调用abort_with_reason
函数来中断程序运行。
详见:
iOS 底层解析weak的实现原理(包含weak对象的初始化,引用,释放的分析)
ARC下dealloc过程及.cxx_destruct的探究