面试题
1、以下代码有什么问题吗?如果没有问题的话,obj、obj2的引⽤用计数分别是多少?如果
有问题的话存在什么问题?
Class *obj = [[Class alloc]init];
Class *obj2 = obj;
[obj hello];
[obj release];
[obj2 hello];
[obj2 release];
答:上⾯面的代码是存在问题的。当执⾏行完第⼀一⾏行代码时,因为执⾏行了alloc,obj的引⽤用计
数为1。第⼆二⾏行代码执⾏行完之后,obj2只是和obj指向了同⼀一块内存。第三⾏行代码是执⾏行了
hello⽅方法。第四⾏行代码执⾏行release消息之后,obj的引⽤用计数减⼀一,这时retainCount变为
0.系统⾃自动调⽤用dealloc⽅方法,对象被销毁。第五⾏行代码执⾏行时,obj2执⾏行的内存已经被系
统回收了,但还是调⽤用了hello⽅方法,出现了问题(野指针)。第六⾏行执⾏行时,obj2所指向
的内存已经不存在,再次调⽤用release消息,出现过度释放的问题,⽽而且obj2已经变成野指
针了。
Class *obj = [[Class alloc]init];//obj引⽤用计数加1
Class *obj2 = obj;//obj,obj2指向同⼀一块内存(对象)
[obj hello];
[obj release];//obj指向的内存(对象)被销毁
[obj2 hello];//错误,obj2指向的内存(对象)已经被销毁了
[obj2 release];//[obj release]之后,obj2是个野指针,不应该再去调⽤用⽅方法.
2、在实际开发的过程中,什么情况下需要创建⾃自动释放池?下⾯面代码中有没有什么问
题?
Person *p1=[[Person alloc]init];
@autoreleasepool {
[p1 autorelease];
@autoreleasepool {
[p1 autorelease];
}
"1
}
答:其实⾃自动释放池存在的意义是为了延迟释放⼀一些对象,延迟向对象发送release消
息。在实际的开发中呢,有两种情况是需要⼿手动创建⾃自动释放池的。第⼀一个就是在多线程
中,因为⼦子线程中可能会使⽤用便利构造器等⽅方法来创建对象,那么这些对象的释放只能放
在⾃自动释放池中,主线程其实已经添加过⾃自动释放池,在main函数⾥里⾯面。第⼆二个就是如果
⼀一段代码⾥里⾯面(⽐比如for循环)⼤大量使⽤用便利构造器创建对象,也需要⼿手动添加⾃自动释放
池。
上述代码其实是存在问题的。当执⾏行完第⼀一⾏行代码时,p1的引⽤用计数是1.第⼆二⾏行是创建了
⼀一个autoreleasepool。第三⾏行代码向p1发送了autorelease消息,延迟release,即在出池
的时候,把p1释放掉。第四⾏行代码⼜又创建了⼀一个autoreleasepool。第五⾏行代码再次向p1
发送了autorelease消息。当代码执⾏行到第六⾏行的“}”时,第⼆二个⾃自动释放池结束,这时p1
引⽤用技术减1,p1所指向的内存(对象)的retainCount由1变为0,内存被系统回收。代码
执⾏行到第七⾏行时,最外层的⾃自动释放池结束,再次向p1发送release消息,出现过度释
放。
@autoreleasepool {
[p1 autorelease];//此时p1被加⼊入⾃自动释放池1
@autoreleasepool {
[p1 autorelease];//此时p1被加⼊入⾃自动释放池2
}//此处,⾃自动释放池2结束,p1引⽤用计数-1
}//此处,⾃自动释放池1结束,但是向已经被释放的对象p1发送了消息
3、ARC下dealloc⽅方法存在的意义在于什么地⽅方?举例说明⼀一下具体的使⽤用场景。
答:其实在MRC中dealloc⽅方法存在的主要意义是为了释放⾃自⾝身的实例变量,移除观察者,停⽌止
timer,移除通知,代理置空等。ARC下,系统会帮助我们释放该对象所包含的实例变量,但是有
些对象还是需要们⾃自⼰己去释放的(⽐比如Core Foundation框架下的⼀一些对象),另外通知中观察者
的移除,代理置空,停⽌止timer等
⽰示例如下所⽰示:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];//
移除通知观察者
[[XMPPManager sharedManager]
removeFromDelegateQueue:self];//移除委托引⽤用
[[MyClass shareInstance] doSomething ]//其他操作
scrollView.delegate = nil;
[timer invalidate];
}
4、分别写出MRC中在assign、retain、copy下属性name对应的setter⽅方法的内部实现。
答:
assign下
- (void) setName:(NSString*)name
{
_name = name;
}
retain下
- (void) setName:(NSString*)name
"2
2015年11⽉月9⽇日 星期⼀一
{
if(_name != name){
[_name release];
_name = [name retain];
}
}
copy下
- (void) setName:(NSString*)name
{
if(_name != name){
[_name release];
_name = [name copy];
}
}
5、在Category中本⾝身不允许为已有的类添加新的属性或者成员变量,你有没有其他的⽅方法可以在
category中添加属性或者是成员变量?
答:⼀一种⽅方法就是使⽤用runtime.h中的objc_getAssociatedObject和objc_setAssociatedObject来访
问和⽣生成关联对象。例如为NSObject添加⼀一个类⺫⽬目,分类中添加⼀一个属性。代码如下所⽰示:
NSObject+Test.h⽂文件
#import <Foundation/Foundation.h>
@interface NSObject (Test)
@property (nonatomic, strong) NSString *test;
@end
NSObject+Test.m⽂文件
#import “NSObject+Test.h"
#import <objc/runtime.h>
static const void *instanceNameKey = &instanceNameKey;
@implementation NSObject (Test)
@dynamic test;
- (NSString *)test
{
return objc_getAssociatedObject(self, instanceNameKey);
}
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, instanceNameKey, test,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
6、@synthesize和@dynamic有什么区别?
(1)@property有两个对应的词,⼀一个是@synthesize,⼀一个是@dynamic。如果@synthesize和
@dynamic都没写,那么默认的就是@syntheszie var = _var;
(2)@synthesize的语义是如果你没有⼿手动实现setter⽅方法和getter⽅方法,那么编译器会⾃自动为你加
上这两个⽅方法。
(3)@dynamic告诉编译器:属性的setter与getter⽅方法由⽤用户⾃自⼰己实现,不⾃自动⽣生成。(当然对于
readonly的属性只需提供getter即可)。假如⼀一个属性被声明为@dynamic var,然后你没有提供
"3
2015年11⽉月9⽇日 星期⼀一
@setter⽅方法和@getter⽅方法,编译的时候没问题,但是当程序运⾏行到instance.var = someVar,由
于缺setter⽅方法会导致程序崩溃;或者当运⾏行到 someVar = instance.var时,由于缺getter⽅方法同样
会导致崩溃。编译时没问题,运⾏行时才执⾏行相应的⽅方法,这就是所谓的动态绑定。@dynamic可⽤用
于在分类中添加属性(需要⽤用到objc_getAssociatedObject和objc_setAssociatedObject函数)。
7、你如何看待iOS中的拷⻉贝?
答:在我看来,⽇日常⽣生活中,当我们⽤用到”拷⻉贝”这个词语的时候,不管怎样都会产⽣生两
份。⼀一份是原来的,⼀一份是新拷⻉贝出来的。但是到⺫⽬目前为⽌止,在iOS中我看到了三种拷⻉贝
⽅方式:
(1)伪拷⻉贝:伪拷⻉贝,顾名思义,就是假拷⻉贝,没有拷⻉贝出新的对象。这⼀一点对于NSString
这种类簇来说⽐比较常⻅见,NSString本⾝身是不可变字符串,内容不可能被修改,因此我们也
没有拷⻉贝的必要,因为拷⻉贝的⺫⽬目的是防⽌止原件被修改,所以才拷⻉贝出来⼀一份,伪拷⻉贝实际
上是对象引⽤用计数加了1(相当于retain或者strong的功效)。
(2)浅拷⻉贝:浅拷⻉贝就是确实拷⻉贝出来⼀一份和原来内容⼀一样的新对象。但是对于对象⾃自带的
属性是伪拷⻉贝,两个对象的属性指向同⼀一个内存。
(3)深拷⻉贝:深拷⻉贝就是不仅仅拷⻉贝出⼀一份新对象,⽽而且对象的属性也拷⻉贝了⼀一份。
总的来说,如果在开发的过程中需要实现拷⻉贝,那么需要接受NSCopying协议。实现
copyWithZone:⽅方法。浅拷⻉贝、深拷⻉贝的区别在于copyWithZone:⽅方法的实现不同。
8、⽗父类实现深拷⻉贝时,⼦子类如何实现深拷⻉贝 ?⽗父类没有实现深拷⻉贝时,⼦子类如何实现深
度拷⻉贝?
答:⽗父类实现深拷⻉贝之后,⼦子类在重写的copyWithZone⽅方法,先调⽤用⽗父类的
copyWithZone⽅方法,之后实现⾃自⼰己属性的拷⻉贝。
如果⽗父类没有实现深拷⻉贝,⼦子类除了需要对⾃自⼰己的属性进⾏行拷⻉贝,还要对⽗父类的属性进⾏行
拷⻉贝。
9、系统中有哪些单例类?在真实的开发中,单例的应⽤用场景在于什么地⽅方?
答:系统为我们提供了很多单例类。例如:UIScreen、UIDevice、NSFileManager、
NSNotificationCenter,NSUserDefaults,UIApplication等等。⽽而且在实际的开发中除了
使⽤用系统给我们提供的单例类之外,还会根据需求⾃自⼰己创建单例类。例如:1.数据库处
理。对于数据的增删改查,可能在很多界⾯面都会⽤用到,那这个时候,可以定义⼀一个针对数
据处理的单例类。2.⾳音乐播放。在做⾳音乐播放类项⺫⽬目时,⼀一般会在多个⻚页⾯面都可以播放⾳音
乐,这时也可以讲播放器定义为单例类,在多个界⾯面使⽤用。3.下载管理,当下载⾳音乐或者
视频时,应该定义⼀一个单例类管理下载任务。4.登录类程序⾥里⾯面,还可以定义单例类保存
⽤用户资料,状态等。
10、写出在多线程情况下的⼀一个单例。
答:⼀一般情况下,在开发中我们所写的单例都是伪单例。即只是保证了在调⽤用某⼀一⽅方法时,
所产⽣生的对象只有⼀一个,但是没有考虑其他的影响其引⽤用计数的⽅方法。例如retain、copy
等。为了保证线程安全,单例的写法如下所⽰示:
"4
2015年11⽉月9⽇日 星期⼀一
⽅方法⼀一:
static AccountManager *sharedAccountManagerInstance = nil;
+ (AccountManager *)sharedManager{
@synchronized (self){
if (sharedAccountManagerInstance == nil) {
sharedAccountManagerInstance = [[AccountManager alloc] init];
}
}
return sharedAccountManagerInstance;
}
⽅方法⼆二:
static AccountManager *sharedAccountManagerInstance = nil;
+ (AccountManager *)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedAccountManagerInstance = [[AccountManager alloc] init];
});
return sharedAccountManagerInstance;
}
11、写⼀一个标准的宏,这个宏输⼊入两个参数并且返回较⼩小的⼀一个。
答:#define Min(X,Y) ((X) < (Y) ?(X):(Y))
12、应⽤用程序在启动的时候主要做了什么操作?
答:应⽤用程序在启动的时候,会执⾏行main函数,⽽而main函数⾥里⾯面主要执⾏行了
UIApplicationMain函数。UIApplicationMain函数执⾏行完主要做了以下三个操作:
(1)创建应⽤用程序UIApplication对象。
(2)创建了应⽤用程序代理对象。默认的应⽤用程序代理对象是AppDelegate。
"5
2015年11⽉月9⽇日 星期⼀一
(3)建⽴立⼀一个事件循环RunLoop。⽤用来实时监测应⽤用程序中的各种事件(触摸,晃动,远
程控制事件,通知,观察者,timer等等)。
13、简述应⽤用程序按Home键进⼊入后台时的⽣生命周期,以及从后台回到前台时的⽣生命周
期。
答:正在运⾏行的程序,点击了Home键之后,应⽤用程序会由活跃状态变为⾮非活跃状态,之
后应⽤用程序进⼊入后台。在这个过程中执⾏行的⽅方法分别是applicationWillResignActive:和
applicationDidEnterBackground:。
当应⽤用程序从后台回到前台时,应⽤用程序会先进⼊入前端,然后由⾮非活跃状态变为活跃
状态。在这个过程中执⾏行的⽅方法分别是:
applicationWillEnterForeground:和applicationDidBecomeActive:
14、ViewController 的 alloc,loadView,viewDidLoad,dealloc在⾃自定义ViewController
中这⼏几个函数⾥里⾯面应该做什么⼯工作?
答: alloc⽅方法⼀一般情况下会直接调⽤用,主要是为了开辟内存空间,创建对象。
重写loadView⽅方法的主要⺫⽬目的是使⽤用⾃自⼰己创建的view(⼀一般会定义⼀一个UIView⼦子
类,⽤用⼦子类创建对象,⼦子类中已经添加好对应的控件了)作为ViewController的View,⽽而
不是直接使⽤用系统⾃自带的View。loadView⽅方法和我们在storyboard或XIB中拖控件是等价
的。
viewDidLoad⽅方法⼀一般情况下会给添加好的控件赋值(⽐比如⻚页⾯面传值时,列表⻚页⾯面
会把model传递给detail⻚页⾯面,在detail⻚页⾯面的viewDidLoad中使⽤用model中的数据给控件赋
值),开启timer,⺴⽹网络请求等。
dealloc⽅方法在执⾏行的时候,主要是释放控制器的实例变量或者是将delegate置空、
移除观察者,停⽌止timer等。
15、请描述⼀一下viewController⼏几个重要⽅方法的执⾏行时机
答:视图控制器的⽣生命周期分为创建、 显⽰示 和 销毁。
(1)创建
xxx *x = [[xxx alloc] init] // 作⽤用:分配内存空间,创建控制器时使⽤用
(2)加载和显⽰示:
- (void)loadView: // 作⽤用:加载根视图(即为self.view赋值) ,在控制器view属性的getter
⽅方法⾸首次调⽤用的时候执⾏行此⽅方法。
根视图已加载:
- (void)viewDidLoad// loadView执⾏行完毕之后,⽴立即执⾏行此⽅方法。作⽤用:给创建好的控件
设置值,做⺴⽹网络请求,开启timer等
"6
2015年11⽉月9⽇日 星期⼀一
loadView 和 viewDidLoad 当⾸首次访问controller的根视图时才会执⾏行,即view属性的getter
⽅方法⾸首次使⽤用时执⾏行, loadView 执⾏行在前, viewDidLoad执⾏行在后。
如果重写了 loadView ⼀一定要给控制器的根视图赋值(self.view = xxView;),在给控制
器的根视图赋值的之前,不能使⽤用控制器view属性的getter⽅方法,否则会重复执⾏行
loadView 和 viewDidLoad ⽅方法。
视图将要显⽰示:
- (void)viewWillAppear:
视图将要被添加到 UI 层级上,还没有添加时执⾏行。(此时视图还没有显⽰示)
视图已经显⽰示:
- (void)viewDidAppear
视图已经被添加到 UI 层级上,视图已经显⽰示出来时执⾏行。(视图已经显⽰示)
视图将要消失:
- (void)viewWillDisappear:
视图将要被从 UI 层级上⾯面移除,但视图还没有移除时执⾏行。 (视图还可⻅见)
视图已经消失:
- (void)viewDidDisappear:
视图已经从 UI 层级上⾯面移除,视图已经不可⻅见时执⾏行。 (视图不可⻅见)
(3)销毁:
- (void)viewWillUnload
iOS6.0之后已经被弃⽤用。iOS 5.0 之前,当低内存且控制器的view不需要使⽤用的时候会调
⽤用这个⽅方法,即当控制器的根视图 将要 被释放时执⾏行([vc.view release],vc.view还未置
空),我们可以在这个⽅方法中移除⼀一些跟视图相关的观察者和通知,并记录视图的状态,
以便之后重新创建视图。 iOS6.0 之后不再需要做释放了,该⽅方法也被遗弃了。
- (void)viewDidUnload
iOS6.0 之后已经被弃⽤用。iOS 5.0 之前,当低内存且控制器的view不需要使⽤用的时候会调
⽤用这个⽅方法,即当控制器的根视图被释放时执⾏行([vc.view release],vc.view已经为
nil),这个⽅方法给我们⼀一个机会做内存的相关清理⼯工作,如果控制器对某些视图有引⽤用,
可以在这⾥里释放这些引⽤用,同样可以释放⼀一些懒加载的对象,但是不要释放那些不容易重
新加载的数据。 iOS6.0 之后不再需要做释放了,该⽅方法也被遗弃了。
16.在使⽤用系统的⼀一些类例如UITableView时,会发现其delegate属性的语义设置为assign
⽽而不是retain,为什么呢?
"7
2015年11⽉月9⽇日 星期⼀一
答:delegate 的语义修饰符 为assign 不是 retain 主要是为了避免两个对象因相互引⽤用造
成的内存泄露问题。
因为 retain修饰delegate,当通过set⽅方法赋值时,对象的引⽤用计数会+1.
如果此时两个对象具有如下关系:A对象有⼀一个属性是B,B的delegate是A。即互相为彼
此的属性。例如: A 是viewController ,B是tableView. B 是A的属性,B的引⽤用计数会
+1,B的delegate是A,A 的引⽤用计数也会 +1. 那么如果A没有释放,B就⼀一直不会释放,
因为A对B有拥有权。 同理,B没有释放,A也⼀一直释放不掉,因为B对A也有拥有权。 因
此A 和 B 的dealloc 都会不执⾏行,造成内存泄露.
代码演⽰示如下(只写出了核⼼心代码):
A *a = [[A alloc] init] // a 引⽤用计数:1
B *b = [[B alloc] init] // b 引⽤用计数:1
a.b = b; // b 引⽤用计数: 2
b.delegate = a; // a 引⽤用计数: 2
[a release]; // a引⽤用计数: 1
[b release]; // b引⽤用计数: 1
// 最终 A 和 B 都释放不掉。
assign则是直接赋值,并不会引起引⽤用计数增加,因此使⽤用assign 不会出现内存泄露。
代码演⽰示如下(只写出了核⼼心代码):
A *a = [[A alloc] init] // a 引⽤用计数:1
B *b = [[B alloc] init] // b 引⽤用计数:1
a.b = b; // b 引⽤用计数: 2
b.delegate = a; // a 引⽤用计数: 1
[a release]; // a引⽤用计数: 0, b引⽤用计数:1(在a的dealloc⾥里⾯面_b被释放了)
[b release]; // b引⽤用计数: 0
// 最终 A 和 B 都可以释放掉。
17.UIImage初始化⼀一张图⽚片的⽅方法以及优缺点
答:(1)UIImage *image = [UIImage imageNamed:@”1.png”];
这个⽅方法创建的图⽚片是从缓存⾥里⾯面获取的, 先在缓存⾥里⾯面查,看是不是有这个图⽚片, 没有的
话将图⽚片添加进缓存再使⽤用. 有的话直接使⽤用缓存⾥里⾯面的. 在程序中,如果这张图⽚片要在多
"8
2015年11⽉月9⽇日 星期⼀一
个地⽅方使⽤用的话, 建议使⽤用这种⽅方式. 缺点是⼀一旦加⼊入缓存,就⼀一直占⽤用内存,不能被释放
掉。
(2)//读取本地图⽚片路径
NSString *imagePath=[NSString stringWithFormat:@"%@/Documents/
%@.jpg",NSHomeDirectory(),@"test"];
[UIImage imageWithContentsOfFile:imagePath];
从⼿手机本地读取, ⽐比较第⼀一种⽅方式, 这个是直接加载图⽚片的,图⽚片不需要的时候,可以
release掉. 所以建议在使⽤用重复率低的图⽚片时使⽤用这个⽅方法.
(3) // 以下这种⽅方式会卡线程,建议代码放到⼦子线程⾥里⾯面。
// imageWithData: data可以通过data创建图⽚片(data可以来⾃自⺴⽹网络也可以来⾃自本地)
NSURL *url = [NSURL URLWithString:@“http://e.picphotos.baidu.com/album/s
%3D550%3Bq%3D90%3Bc%3Dxiangce%2C100%2C100/
sign=f51d2708cb11728b342d8c27f8c7b2f3/
bba1cd11728b47109d0f5555c5cec3fdfc032302.jpg?
referer=f55e30e2d71373f0ac285aaf8d00&x=.jpg"];
UIImage *image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
需要注意的是,如果imageWithData是同步⺴⽹网络请求,如果在主线程中直接使⽤用,会卡主
线程,因此⼀一般不会在主线程中直接使⽤用,⽽而是采⽤用异步⺴⽹网络请求获取data赋值。
18.当⼿手指点击屏幕上的登录按钮时,响应者链的检测过程是什么样⼦子的?
答:当⼿手指触摸到屏幕上的登录按钮时,⾸首先runloop会检测到事件,将事件传递给
UIApplication对象,UIApplication对象将事件传递给AppDelegate对象,之后
AppDelegate对象将事件传递给window对象,然后传递给rootViewController,之后是view,
之后检测View上的⼦子视图,通过⽐比对触摸的位置定位到对应的Button。
19.在⼀一个imageView上添加Button,给Button添加⼀一个事件,点击Button时能否响应事
件?如果不能,⽤用响应者链解释⼀一下为什么不可以?
答:不能。因为imageView的⽤用户交互默认是关闭的。imageView的⽤用户交互关闭,阻断
了响应者链的检测过程,所以在检测的过程中,就直接不检测imageView以及它上⾯面的其
他控件,也就检测不到button的存在。因为响应的过程正好是和检测的过程相反,因为检
测不到button被触摸,所以button的点击事件也不会响应。解决办法就是把imageView的
⽤用户交互打开。
20.列举⼀一下常⽤用的第三⽅方框架
答:我在做开发的过程中曾经⽤用过以下第三⽅方框架:
(1)AFNetworking ⺴⽹网络请求库
(2)SDWebImage ⺴⽹网络图⽚片加载
"9
2015年11⽉月9⽇日 星期⼀一
(3)FMDB 数据库
(4)Masonry ⾃自动布局
(5)UmengSDK/Share SDK 分享
(6)融云/环信 即时通信
(7)ZBar/ZXing ⼆二维码扫描和⽣生成
(8)MBProgressHUD ⻛风⽕火轮
(9)GTMBase64 base64编码解码
(10)MJRefresh 下拉刷新,上拉加载
(11)百度/⾼高德地图SDK
21.MJRefresh 原理
答:给UISCrollView添加了分类,在分类中,根据scrollView的contentOffset进⾏行判断⽅方向和偏移
量,当偏移量到某⼀一个临界值的时候,开始切换header或者footer的状态,以呈现不同的UI,当偏
移量⼤大于临界值并松开⼿手的时候, 执⾏行block回调或者target..action的⽅方法, 主要在block或者
target..action中进⾏行⺴⽹网络请求,请求结束后,调⽤用endRefresh⽅方法,再次更改header或者footer的
状态,使他们回到初始状态。
22.block在定义成属性时应该⽤用什么关键字,为什么?
答:block在定义成属性时关键字应该使⽤用copy。我们平时使⽤用的block,主要是存放在栈
区的(有的是在全局区),栈区的block出了作⽤用域就会被释放,当我们进⾏行block回调的
时候,block已经被系统给销毁了,会出现crash。为了管理和使⽤用block,需要将其拷⻉贝到
堆区。全局区的block理论上是不⽤用拷⻉贝的,但拷⻉贝也没什么坏处,不⽤用的时候,我们也
会对copy的对象进⾏行释放,不会造成内存泄露。因为block在栈区的情况⽐比较多,为了书
写⽅方便,保持格统⼀一,我们把block定义成属性时,使⽤用copy关键字。
23.定义⼀一个返回值为字符串类型,参数是int类型的Block。并且调⽤用该Block。
答:Block定义如下所⽰示:
NSString * (^block)(int a) = ^ NSString * (int a){
return [NSString stringWithFormat:“%d”,a];
};
Block调⽤用:
NSString *str = block(10);
24. 请谈谈你对block和delegate模式认识?
答:⽆无论是block还是delegate模式本质上都是回调,使⽤用block,其优点是回调的block代
码块直接就放在了block赋值的地⽅方,使代码更为紧凑,缺点是block内使⽤用到当前类的实
例变量的时候,需要注意循环引⽤用的问题,即需要使⽤用__block(MRC下)或者
__weak(ARC下)定义⼀一个弱引⽤用的self出来,block⾥里⾯面使⽤用弱引⽤用的self去操作属性或调
⽤用⽅方法。delegate模式不⽤用像block⼀一样做特殊处理,但是如果多个对象设置的代理是同⼀一
个对象,就需要在delegate⽅方法中判断当前执⾏行代理的是哪个对象。
25.什么是沙盒?
"10
2015年11⽉月9⽇日 星期⼀一
答:所谓的沙盒其实是操作系统为应⽤用程序分配的⼀一个密闭⽂文件夹。应⽤用程序拥有这个⽂文
件夹内⽂文件的访问权限,且只能对这个⽂文件夹内的⽂文件进⾏行操作(当然也可以访问系统提
供的⽂文件,⽐比如:相册),不可以去访问其他应⽤用程序的⽂文件夹。
26.在沙盒中有⼏几个⽂文件夹?
答:沙盒含有3个⽂文件夹:Documents, Library 和 tmp
Documents:苹果建议将程序中建⽴立的或在程序中浏览到的⽂文件数据保存在该⺫⽬目录下,
iTunes备份和恢复的时候会包括此⺫⽬目录。
Library:存储程序的默认设置或其它状态信息。
iTunes在与iPhone同步时,备份所有的Documents和Library⽂文件。
Library/Caches:存放缓存⽂文件,⼀一般是下载的图⽚片和视频,iTunes不会备份此⺫⽬目录,此
⺫⽬目录下⽂文件不会在应⽤用退出删除。
tmp:临时⽂文件夹,应⽤用程序在重启时,会丢弃所有的tmp⽂文件。
27.你熟悉的设计模式有哪些,请说说对他们的理解。
答:单例,通知,KVO,代理,target-action等
单例,主要特点是⼀一个类只有⼀一个对象。对外提供了⼀一个获取唯⼀一对象的⽅方法,⼀一般都是
类⽅方法,完整的单例会重写很多引⽤用计数相关的⽅方法(⽐比如:allocWithZone,
copyWithZone,retain,release,autorelease,retainCount等)以保证对象在任何情况
下都唯⼀一。单例最⼤大的价值就是建⽴立程序级别的全局变量,就是把不同模块都要⽤用的变量
以属性的形式放到单例⾥里⾯面,以便随时使⽤用。⾳音频播放类程序⼀一般会写单例来管理需要播
放的⾳音乐信息,需要下载的地⽅方也会以单例的形式来管理下载对象。
通知,是M与C通信的⽅方式之⼀一。⼀一般是Model发送变化的时候,会发送通知告诉
Controller,Controller再做相应的处理。需要先往通知中⼼心⾥里⾯面注册观察者,然后在合适
的时机,通知中⼼心post通知,观察者做对应的处理,当观察者将要释放的时候,从通知中
⼼心移除观察者。
KVO,也是M与C通讯的⽅方式。⼀一般是C去观察M的某个属性,某个属性发⽣生变化之后,C
做出相应的处理,当C将要释放的时候,M移除观察者。
代理,是V与C通信的⽅方式之⼀一。⼀一般C是V的代理,V发⽣生变化的时候,C做对应的调整。
例如:UITableView被点击了,由Controller做处理。我们在⾃自⼰己写代理的时候,⼀一定要清
楚谁来处理事件,谁来调⽤用代理⽅方法。通常情况下都是C处理时间,V在适当的时候调⽤用
代理⽅方法。
target-action,也是V与C通信的⽅方式之⼀一。⼀一般C是V的target,V被点击或者被滑动之
后,C做出对应的处理。⽆无论是target-action还是代理,核⼼心都是回调。
28.NSNotification和KVO的区别和⽤用法是什么?什么时候应该使⽤用通知,什么时候应该使⽤用
KVO,它们的实现上有什么区别?
"11
2015年11⽉月9⽇日 星期⼀一
答:通知和KVO都是观察者模式的体现,⼆二者侧重点有些不同。
通知往往⽤用于1对多的场景,多个观察者观察⼀一个通知,⼀一旦这个通知被通知中⼼心post,
所有观察者都可以做出响应。具体的实现流程是:(1)通知中⼼心通过addObserver⽅方法添加
观察者,其实是把观察者和通知进⾏行绑定,多次使⽤用addObserver⽅方法可以为同⼀一个通知
添加多个观察者。(2)通知中⼼心发送通知。(3)各个观察者做各⾃自的处理。(4)在观察者销毁
之前(dealloc中),从通知中⼼心移除观察者。
KVO多⽤用于1对1的场景,⼀一个对象去观察另外⼀一个对象的属性值有没有发⽣生变化,⼀一旦发
⽣生变化,观察者做出响应。具体的流程是:(1)被观察者添加观察者,指定观察者观察的属
性。(2)被观察者的属性在某⼀一时间发⽣生了变化。(3)观察者做出响应。(4)在观察者销毁之
前,先移除观察者。
KVO其实也可以1对多,就是多个观察者观察同⼀一个对象同⼀一个属性的变化。KVO和通知
给⼈人的感觉⼀一个主动通知(通知会由通知中⼼心主动post),⼀一个是被动通知(KVO,观察
者⼀一直盯着属性变没变,⼀一旦变化,⾃自⼰己就做出响应。被观察的对象不是主动告知你我变
了)。
29.定义属性时,什么情况使⽤用copy,assign,和retain?
答:assign⽤用于简单数据类型,如NSInteger,double,bool。retain 和copy⽤用于对象类型,
⼆二者⼜又有区别,copy是⽤用于⼀一个对象有可能被修改,但不想修改原件,所以拷⻉贝⼀一份出来
(新拷⻉贝的对象引⽤用计数为1),这样新拷⻉贝的修改了,原件不会变,原件修改了,新拷
⻉贝的不会变。⽽而retain呢,对象引⽤用计数+1,对象只有⼀一个,但引⽤用计数为2,通过任何⼀一
个引⽤用修改对象内容,另外⼀一个引⽤用使⽤用的时候,⽤用的就是修改之后的内容。
30.在项⺫⽬目中什么时候选择使⽤用GCD,什么时候选择使⽤用NSOperation?
答:⽆无论是GCD还是NSOperation其实都是多线程的⼀一种实现形式。严格的说
NSOperation和线程并没有必然联系,更不是多线程,NSOperation只是操作,封装了
target和action 或者Block,在主线程中执⾏行Operation,Operation就会运⾏行在主线程中,
在⼦子线程中执⾏行Operation,Operation就运⾏行在⼦子线程中。它只有和NSOperationQueue
联合使⽤用的时候,才能发挥出价值。NSOperationQueue是Operation的管理者,它⾸首先是
⼀一个队列,先来的任务先开始执⾏行,后来的任务后开始执⾏行,这些任务的执⾏行是并发的,
NSOperationQueue负责为任务开辟线程以及关闭线程,有点类似于cell重⽤用,⽤用有限的
线程执⾏行任意数量的任务,同时可以设置最⼤大并发数,控制程序的性能,不⾄至于全部任务
⼀一起执⾏行。GCD出现⽐比NSOperationQueue要晚,是⼀一套基于C函数的API,由于是C函
数,所以性能⽐比较⾼高,使⽤用灵活,安全,当然功能也强⼤大。但是相对于GCD来讲,
NSOperationQueue对线程做了封装,使⽤用⽐比较简单,尤其是不⽤用管理线程的开启和关
闭。个⼈人认为,如果仅仅是想要实现异步执⾏行任务,⾸首选GCD,如果要管理多个异步任
务,NSoperationQueue⽐比较⽅方便。如果要做更复杂的处理,⽐比如前5个任务并发,5个任
务执⾏行完之后,需要串⾏行执⾏行3个任务,之后再并发执⾏行若干任务,就需要使⽤用GCD了。
"12
2015年11⽉月9⽇日 星期⼀一
总的来说,特别简单的和⾮非常复杂的多线程任务GCD⽐比较适合,介于⼆二者之间的⽤用
NSOperationQueue⽐比较合适。
31.tableView必须实现的两个⽅方法是什么(⼿手写)?在哪个协议中声明的?
答:在UITableViewDataSource协议中声明的两个必须实现的⽅方法
1.-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
2.-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
32.UITableViewController 中,创建UITableViewCell时,initWithSytle:resuseIdentifier:
中,reuseIdentifier有什么⽤用?简述UITableViewCell的复⽤用原理.
答:只有在cell被滑动出界⾯面的时候,此cell才会被加⼊入到复⽤用队列中。每次在创建cell的
时候,程序会⾸首先通过调⽤用dequeueReusableCellWithIdentifier:cellType⽅方法,到复⽤用队
列中去寻找标⽰示符为“cellType”的cell,如果找不到,返回nil,然后程序去通过调⽤用
[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]来创建
标⽰示符为“cellType”的cell。
33.#include、#import、@class这三个关键字是什么指令?
答:是预编译指令。所谓的预编译指令指的就是在编译之前,先去执⾏行的指令。
34.在oc中引⼊入头⽂文件使⽤用的关键字是哪⼀一个?能在c语⾔言⽂文件中使⽤用吗?
答: #import关键字可以在OC中使⽤用,不能在C⽂文件中使⽤用, #include可以在C和OC中使
⽤用。
35.#import与#include相⽐比,好处是什么?
答: #import确定⼀一个⽂文件只能被导⼊入⼀一次,避免的重复导⼊入的问题,使⽤用#include⼀一定
要注意重复导⼊入的问题。所以在OC中都使⽤用#import来引⽤用头⽂文件。
36.#import<>和#import””的区别是什么?
答:#import<>⽤用于对系统⽂文件的引⽤用,编译器会在系统⽂文件⺫⽬目录中去查找⽂文件
#import””⽤用于对⾃自定义的⽂文件的引⽤用,编译器⾸首先回去⽤用户⺫⽬目录下查找,然后去安装⺫⽬目
录,最后去系统⺫⽬目录中查找⽂文件。
37.@class的作⽤用是什么?
答:@class的作⽤用是告诉编译器有@class后⾯面的内容是⼀一个类名。只是告诉编译器存在
这么⼀一个类,类具体包含哪些⽅方法,属性和变量的并没有告诉编译器。⼀一般在类的头⽂文件
中使⽤用@class来引⼊入其他类。
"13
2015年11⽉月9⽇日 星期⼀一
38.多线程的优点和缺点分别是什么?
答:优点:1、将耗时较⻓长的操作(⺴⽹网络请求、图⽚片下载、⾳音频下载、数据库访问等)放
在⼦子线程中执⾏行,可以防⽌止主线程的卡死;2、可以发挥多核处理的优势,提升cpu的使
⽤用率。
缺点:1、每开辟⼀一个⼦子线程就消耗⼀一定的资源; 2、会造成代码的可读性变差;3、
如果出现多个线程同时访问⼀一个资源,会出现资源争夺的情况
39.NSOperationQueue中有⼀一个属性叫maxConcurrentCount即最⼤大并发数。这⾥里所谓的
最⼤大并发数指的是什么含义?
答:这⾥里的最⼤大并发数指得是在队列中同时执⾏行的任务的个数。有很多⼈人会认为是所分配
的线程的个数。其实不是的。因为线程的个数的多少取决于系统,系统会分配合适的线程
数量来保证这些任务并发执⾏行的,作为程序员我们是没办法控制的。
40.请写出下⾯面代码的打印结果:
NSLog(@"1”);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3”);
答:只能打印1.第⼀一⾏行代码按照顺序会打印1.当执⾏行到第⼆二段代码的时候出现了问题。因
为在主线程中使⽤用了同步,这时为了执⾏行block内部的代码需要阻塞当前线程去执⾏行
Block,但是由于block也是在主线程⾥里⾯面执⾏行,主线程当前还有任务没执⾏行完,产⽣生主线
程死锁现象。代码没办法继续执⾏行。
41.项⺫⽬目中,在什么情况下会⽤用到多线程?
答:多线程处理包括Core Data的多线程访问,耗时的数据计算,数据库访问,异步⺴⽹网络
请求以及⼀一些在运⾏行态内存吃紧的情况下处理⼤大⽂文件的⽅方案等。
42.iOS中哪些数据持久化的⽅方式,各有什么特点,iOS平台怎么做数据的持久化,CoreData和
sqlite有⽆无必然联系?CoreData是⼀一个关系型数据库吗?
答:iOS中可以有四种持久化数据的⽅方式: 属性列表、对象归档、SQLite3和Core Data;
Core Data可以使你以图形界⾯面的⽅方式快速的定义app的数据模型,同时在你的代码中容易
获取到它。core data提供了基础结构去处理常⽤用的功能,例如保存,恢复,撤销和重做,
允许你在app中继续创建新的任务。在使⽤用Core Data的时候,你不⽤用安装额外的数据库系
统,因为core data使⽤用内置的sqlite数据库。Core Data将你app的模型层放⼊入到⼀一组定义
在内存中的数据对象中。Core Data会追踪这些对象的改变,同时可以根据需要做相反的
"14
2015年11⽉月9⽇日 星期⼀一
改变,例如⽤用户执⾏行撤销命令。当Core Data在对你app数据的改变进⾏行保存的时候,
Core Data会把这些数据归档,并永久性保存。
mac os X中sqlite库,它是⼀一个轻量级功能强⼤大的关系数据引擎,也很容易嵌⼊入到应⽤用程
序。可以在多个平台使⽤用,sqlite是⼀一个轻量级的嵌⼊入式sql数据库编程。与core data框架
不同的是,sqlite是使⽤用程序式的,sql的主要的API来直接操作数据表。
Core Data不是⼀一个关系型数据库,也不是关系型数据库管理系统(RDBMS)。虽然Core
Dta⽀支持SQLite作为⼀一种存储类型,但它不能使⽤用任意的SQLite数据库。Core Data在使⽤用
的过程中⾃自⼰己创建这个数据库。Core Data⽀支持对⼀一、对多的关系。
43.id声明的对象有什么特性?
答:id是任意对象类型的,不能表⽰示基本类型。id类型是通⽤用指针类型,因为通过指针,也
就是内存地址来引⽤用对象,所以可以将任意对象赋值给id类型的对象。返回id类型值的⽅方
法是返回指向内存中某对象的指针。然后可以将该值赋给任何对象变量(强制类型转换即
可)。因为⽆无论在哪⾥里,对象总是携带它的isa成员。所以即使将它存储在id类型的通⽤用对
象变量中,也总是可以确定它的真实类型,id是多态的⼀一种体现。
44.对于语句NSString* testObject = [[NSData alloc]init];testObject在编译时和运⾏行时分别
是什么类型的对象?
答:编译时是NSString,运⾏行时是NSData。
45.什么是懒加载?在使⽤用懒加载时的注意事项是什么?
答:所谓的懒加载指的是延迟创建对象,只有当需要的时候才创建对象。在真正开发的过
程中其实懒加载就是重写getter⽅方法。在getter⽅方法的内部,实现对象的创建,如果对象为
nil才创建,如果不为nil,直接返回对象。在真正使⽤用懒加载时需要注意的是当第⼀一次使⽤用
对象时,需要调⽤用self.因为只有这样才能调⽤用对应的getter⽅方法,对象才会被创建。
46.谈谈你对RunLoop的理解。
答:⼀一个程序从main函数开始,函数执⾏行完毕之后就会退出,iOS程序也是⼀一样的,但是
我们从没看到过iOS程序打开之后直接闪退,肯定是有⼀一些东⻄西阻⽌止了程序的退出,最简
单的就是添加⼀一个死循环,RunLoop就是类似于这样的⼀一个死循环,保证你的应⽤用程序不
被退出,区别就是RunLoop会在你的程序有事件(点击事件、摇晃事件等)要处理的时候
才会去让cpu处理,在程序没有事件处理的时候就让系统cpu休眠。在iOS中,每个线程都
有⼀一个RunLoop,但是默认状态下只有主线程的RunLoop是开启的(系统⾃自动帮我们开
启),其他线程开启需要以下代码
BOOL isRunning = NO;
while (!isRunning) {
"15
2015年11⽉月9⽇日 星期⼀一
isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
currentRunLoop 可以获取当前线程的RunLoop , 循环是为了保证能开启RunLoop(系统
繁忙时有可能开启失败)
如果⽤用timerWithTimeInterval来创建NSTimer,我们需要把这个Timer假如到RunLoop才能
执⾏行,如果是在⼦子线程,还需要开启这个RunLoop。
47.简单描述⼀一下RunLoop在实际开发中的应⽤用场景。
答:在做项⺫⽬目时曾遇到过两个问题,需要⼿手动开启runLoop:
1.在⼀一个⻚页⾯面中有⼀一个轮播图和tableView。在滑动tableView的过程中,轮播图不动了。
这时我是⽤用了RunLoop将轮播图的NSTimer加⼊入到了RunLoop中。
2.在⼦子线程中开启了另外⼀一个⼦子线程⽤用于下载图⽚片。这时发现下载图⽚片的代码不执⾏行。通
过在下载图⽚片的代码⾥里⼿手动开启RunLoop,代码才可以执⾏行。
48.什么是Runtime
答:Runtime就是运⾏行时,⼀一个程序开发的过程通常可以分为以下阶段,编辑-预编译-编
译-连接-运⾏行,运⾏行时可以说就是我们的程序再运⾏行的阶段发⽣生的⼀一些事情,在这个阶段
程序通常会把⼀一些OC的代码转化成C语⾔言的代码,从⽽而提⾼高执⾏行的效率,在这个阶段我们
也可以动态的为某个对象的属性赋值,⽽而对象的属性具体是什么类型也会在这个阶段进⾏行
确定(NSString *str = [NSData data]; 其中str在编译的时候是NSString类型,运⾏行的时候
是NSData类型)。系统也提供了Runtime的类库,让我们可以直接调⽤用⼀一些运⾏行时把OC
代码转化C之后的代码⽐比如:objc_msgSend();同样也可以通过运⾏行时,为分类添加属
性,需要⽤用到objc_getAssociatedObject和objc_setAssociatedObject函数。
49.解释这句代码的意思objc_msgSend(array,@selector(insertObject:atIndex:), foo, 5);
答:给array 发送⼀一个insertObject:atIndex: 的消息参数为 foo 和5, 就是给array再下标为
5的地⽅方插⼊入⼀一个foo对象
50.OC的优缺点:
答:
优点:1、Category。可以很⽅方便的为⼀一个已有的类添加属性或者⽅方法,⽽而不需要笨拙的
去继承它。2、posing。可以让⼀一个类的对象动态的以其他类⾏行为去执⾏行,也许可以理解
成动态replace所有的method(消息转发机制,⽐比如是⽤用Person类的⼀一个对象去调⽤用⼀一个
Student类的⽅方法)3、 动态识别。 ⽐比较常⻅见的动态语⾔言的特性,涉及的点就多了,举个
简单的例⼦子,判断⼀一个对象是否是某个类的成员。isKindOfClass: 4、弹性讯息传递 。⽅方
法(method)的动态处理,譬如当你调⽤用⼀一个没有的⽅方法的时候,系统将是再运⾏行时跑出异
常⽽而不是编译时给出错误。5、 不是⼀一个过度复杂的 C 衍⽣生语⾔言 6、 Objective-C 与 C++
"16
2015年11⽉月9⽇日 星期⼀一
可混合编程
缺点: 1、不⽀支持命名空间 (写过c#等其他语⾔言的应该⽐比较清楚,可以通过命名空间将相同
名字的类进⾏行分类,⽽而objc中不得不通过前缀进⾏行区分,这也是为什么苹果的类库都有
UIXXX NSXXX等统⼀一前缀了)2、不⽀支持运算符重载3、不⽀支持多重继承4、使⽤用动态运
⾏行时类型,所有的⽅方法都是函数调⽤用,所以很多编译时优化⽅方法都⽤用不到。(如内联函数
等),性能低劣。
51.写⼀一个冒泡排序
答: int numbers[5] = {4, 14, 88, 22, 60};
int count = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (numbers[j] > numbers[j + 1]) {
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
}
}
}
52.⽤用OC写⼀一个冒泡排序
答: NSMutableArray *numbers = [@[@42, @75, @22, @14, @1, @55]
mutableCopy];
for (int i = 0; i < numbers.count - 1; i++) {
for (int j = 0; j < numbers.count - 1 - i; j++) {
if ([numbers[j] integerValue] > [numbers[j + 1] integerValue]) {
[numbers exchangeObjectAtIndex:j withObjectAtIndex:j + 1];
}
}
}
"17
2015年11⽉月9⽇日 星期⼀一
53.如何优化冒泡排序
答:添加⼀一个BOOL来标识当前的数组是否有序,在外层循环条件增加判断,⽆无序
(NO)的情况下再排序,每次进⼊入循环假设是有序的(YES),⽆无序的时候(进⼊入if条
件)把这个标识设置为⽆无序(NO),如果没有进⼊入if条件就说明当前的数组已经是有序
的,则下次循环的时候会根据添加的条件⾃自动停⽌止循环
int flag = 0;
int numbers[5] = {4, 14, 88, 22, 60};
int count = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < count - 1 && flag == 0; i++) {
flag = 1;
for (int j = 0; j < count - 1 - i; j++) {
if (numbers[j] > numbers[j + 1]) {
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
flag = 0;
}
}
}
54.写⼀一个冒泡排序的函数
void sort(int array[], int count){
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
"18
2015年11⽉月9⽇日 星期⼀一
}
}
}
}
int main(int argc, const char * argv[]) {
int flag = 0;
int numbers[5] = {4, 14, 88, 22, 60};
//如果函数实现在main函数后⾯面需要先声明sort函数
int count = sizeof(numbers) / sizeof(numbers[0]);
sort(numbers, count);
for (int i = 0; i < count; i++) {
printf("%d\n", numbers[i]);
}
return 0;
}
55.简述你对UIView、UIWindow、CALayer的理解。
答:CALayer是图层类,本⾝身可以显⽰示的,但是不能响应事件。
UIView是iOS系统中界⾯面元素的基础,所有的界⾯面元素都继承⾃自它。事件的处理由它
来执⾏行,但是显⽰示其实是由其对应的layer层来操作的,UIView内嵌了⼀一个layer,layer显
⽰示内容,UIView本⾝身增加了事件处理的功能。
UIWindow继承⾃自UIView,主要的作⽤用是作为窗⼝口呈现其他的视图。⽽而且⼀一个应⽤用
程序⼀一般情况下只有⼀一个窗⼝口。
56.在iOS中如何实现国际化?
答:iOS中国际化需要做相关的配置:
(1)选中应⽤用程序对应的project,然后添加所需要国际化的语⾔言。
(2)新建Localizable.strings⽂文件,作为多语⾔言对应的词典,存储多种语⾔言,点击右侧
Localization,勾选国际化对应的语⾔言。
(3)添加⼀一个字段,设置你想要国际化的字段
在English中,添加:SUBMIT_BTN_TITLE = Go;
"19
2015年11⽉月9⽇日 星期⼀一
在Chinese中,添加:SUBMIT_BTN_TITLE = 开始;
57.简单描述你⼀一下在开发的过程中,如何实现程序的性能优化?
答:我在开发的过程中会注意⼀一下⼏几点来优化程序性能:
1.避免庞⼤大的XIB
2.使⽤用懒加载的⽅方式延迟加载界⾯面
3.避免反复处理数据
4.避免使⽤用NSDateFormatter和NSCalendar。
5.图⽚片缓存的取舍
UIImage加载图⽚片⽅方式⼀一般有两种:
A:imagedNamed初始化
B:imageWithContentsOfFile初始化
⼆二者不同之处在于,imageNamed默认加载图⽚片成功后会内存中缓存图⽚片,这个⽅方法⽤用⼀一个
指定的名字在系统缓存中查找并返回⼀一个图⽚片对象.如果缓存中没有找到相应的图⽚片对象,
则从指定地⽅方加载图⽚片然后缓存对象,并返回这个图⽚片对象.
⽽而imageWithContentsOfFile则仅只加载图⽚片,不缓存.
⼤大量使⽤用imageNamed⽅方式会在不需要缓存的地⽅方额外增加开销CPU的时间来做这件事.
当应⽤用程序需要加载⼀一张⽐比较⼤大的图⽚片并且使⽤用⼀一次性,那么其实是没有必要去缓存这个
图⽚片的,⽤用imageWithContentsOfFile是最为经济的⽅方式,这样不会因为UIImage元素较多
情况下,CPU会被逐个分散在不必要缓存上浪费过多时间.使⽤用场景需要编程时,应该根
据实际应⽤用场景加以区分,UIImage虽⼩小,但使⽤用元素较多问题会有所凸显.
58.XMPP的优点和缺点在于什么地⽅方?
答:XMPP是⼀一种即时通讯协议,基于XML的点对点的即时通讯协议。它的优点有:
1.开放:XMPP本⾝身是开放的,所以在客户端、数据库等⽅方⾯面都有很多具体的实现和应
⽤用。
2.安全:
3.可扩展:XML命名空间的威⼒力可使任何⼈人在核⼼心协议的基础上建造定制化的功能
缺点:
只能传输⽂文本,不能传输⾳音频,视频和图⽚片。如果要传输⾳音频,视频和图⽚片,需要通过
http协议传到服务器,服务器返回⼀一个url,这个url再通过XMPP传递给对⽅方,对⽅方拿到之
后,再通过url去下载和显⽰示。
59.如何让你的应⽤用程序更加省电?
"20
2015年11⽉月9⽇日 星期⼀一
答:(1)如果程序⽤用到定位,需要在定位完毕之后关闭定位,或者降低定位的频率,不停的
定位会消耗电量。(2)如果⽤用到了蓝⽛牙,需要使⽤用蓝⽛牙时候开启蓝⽛牙,蓝⽛牙⽤用完之后关闭蓝
⽛牙,蓝⽛牙也很耗电。(3)优化算法,减少循环次数,⼤大量循环会让CPU⼀一直处于忙碌状
态,特别费电。(4)不要使⽤用⺴⽹网络轮询,使⽤用推送。(5)timer的时间间隔不宜太短,满⾜足需
求即可。(5)不要频繁刷新⻚页⾯面,能刷新1⾏行cell,不要reloadData。(6)切勿让屏幕⻓长亮。
(7)线程适量,不宜过多。
60.如何实现程序后台运⾏行?
答:苹果是不赞成程序在后台运⾏行的(因为很费电),除⾮非后台运⾏行能提升
⽤用户体验。有3种情况允许程序后台运⾏行。(1)在前台开启了⼀一个短任务,可
以在程序进⼊入后台的时候,向系统申请点时间把任务执⾏行完。(2)前台有下载
任务,可以在进⼊入后台的时候继续下载。(3)⼀一些特定类型的任务可以在后台
运⾏行,⽐比如:定位,⾳音乐播放,VoIP等等。
对于第⼀一种情况,可以调⽤用UIApplication类的
beginBackgroundTaskWithName:expirationHandler:或者
beginBackgroundTaskWithExpirationHandler:⽅方法申请⼀一点额外的时间去执
⾏行未完成的任务。调⽤用这两个⽅方法中的⼀一个都会临时延迟应⽤用程序进⼊入休眠
的时间,以便有时间把任务执⾏行完。当任务执⾏行完毕之后,应⽤用程序需要调
⽤用endBackgroundTask:⽅方法让系统知道任务执⾏行完了,程序可以进⼊入休眠状
态了。不⼀一定⾮非得等应⽤用程序要进⼊入后台了才设置后台需要执⾏行的任务。⼀一
个好的设计应该是在任务开始前就是⽤用
beginBackgroundTaskWithName:expirationHandler:或者
beginBackgroundTaskWithName:expirationHandler:添加后台执⾏行的任务,
任务⼀一旦执⾏行完就调⽤用endBackgroundTask:⽅方法告诉后台,需要进⼊入后台执
⾏行的任务执⾏行完了(此时可能还没进⼊入后台)。
对于第⼆二种情况,如果要下载⽂文件,应⽤用程序应该使⽤用NSURLSession对象
去开启下载任务,这样系统就可以在程序休眠的时候控制下载,当我们配置
NSURLSession需要在后台传输的时候,系统会单独开⼀一个进程去处理下载
任务,如果在下载的时候,进⼊入了后台,会在后台继续下载。想要实现后台
下载,我们需要配置NSURLSession。⾸首先创建⼀一个
NSURLSessionConfiguration对象,设置⼀一些属性,然后把
NSURLSessionConfiguration对象设置给NSURLSession对象。具体步骤:
(1)使⽤用backgroundSessionConfigurationWithIdentifier:创建
NSURLSessionConfiguration对象。(2)设置NSURLSessionConfiguration对
象的sessionSendsLaunchEvents 属性为YES。(3)如果在前台已经开始下载
了,还需要设置NSURLSessionConfiguration的discretionary属性为YES。
(4)为NSURLSessionConfiguration对象设置其他你需要的属性。(5)把
NSURLSessionConfiguration对象设置给NSURLSession对象。
第三种情况属于Long-Running任务。如果需要实现⻓长时任务,必须去申请,
⽽而且只有特定的类型才能⻓长时间在后台执⾏行。⽐比如:后台播放⾳音频,后台录
⾳音,后台定位和导航,VoIP,定期下载和处理新内容,定期接收外设的数
据。需要在Info.plist添加UIBackgroundModes字段选择需要后台运⾏行的事件
类型,或者在⼯工程的Capabilities⾥里⾯面打开Background Modes,勾选对应的
"21
2015年11⽉月9⽇日 星期⼀一
事件,告诉系统会在后台运⾏行什么类型的Long-Running任务,然后再根据不
同类型的事件做相应的处理即可。
"22
1、以下代码有什么问题吗?如果没有问题的话,obj、obj2的引⽤用计数分别是多少?如果
有问题的话存在什么问题?
Class *obj = [[Class alloc]init];
Class *obj2 = obj;
[obj hello];
[obj release];
[obj2 hello];
[obj2 release];
答:上⾯面的代码是存在问题的。当执⾏行完第⼀一⾏行代码时,因为执⾏行了alloc,obj的引⽤用计
数为1。第⼆二⾏行代码执⾏行完之后,obj2只是和obj指向了同⼀一块内存。第三⾏行代码是执⾏行了
hello⽅方法。第四⾏行代码执⾏行release消息之后,obj的引⽤用计数减⼀一,这时retainCount变为
0.系统⾃自动调⽤用dealloc⽅方法,对象被销毁。第五⾏行代码执⾏行时,obj2执⾏行的内存已经被系
统回收了,但还是调⽤用了hello⽅方法,出现了问题(野指针)。第六⾏行执⾏行时,obj2所指向
的内存已经不存在,再次调⽤用release消息,出现过度释放的问题,⽽而且obj2已经变成野指
针了。
Class *obj = [[Class alloc]init];//obj引⽤用计数加1
Class *obj2 = obj;//obj,obj2指向同⼀一块内存(对象)
[obj hello];
[obj release];//obj指向的内存(对象)被销毁
[obj2 hello];//错误,obj2指向的内存(对象)已经被销毁了
[obj2 release];//[obj release]之后,obj2是个野指针,不应该再去调⽤用⽅方法.
2、在实际开发的过程中,什么情况下需要创建⾃自动释放池?下⾯面代码中有没有什么问
题?
Person *p1=[[Person alloc]init];
@autoreleasepool {
[p1 autorelease];
@autoreleasepool {
[p1 autorelease];
}
"1
}
答:其实⾃自动释放池存在的意义是为了延迟释放⼀一些对象,延迟向对象发送release消
息。在实际的开发中呢,有两种情况是需要⼿手动创建⾃自动释放池的。第⼀一个就是在多线程
中,因为⼦子线程中可能会使⽤用便利构造器等⽅方法来创建对象,那么这些对象的释放只能放
在⾃自动释放池中,主线程其实已经添加过⾃自动释放池,在main函数⾥里⾯面。第⼆二个就是如果
⼀一段代码⾥里⾯面(⽐比如for循环)⼤大量使⽤用便利构造器创建对象,也需要⼿手动添加⾃自动释放
池。
上述代码其实是存在问题的。当执⾏行完第⼀一⾏行代码时,p1的引⽤用计数是1.第⼆二⾏行是创建了
⼀一个autoreleasepool。第三⾏行代码向p1发送了autorelease消息,延迟release,即在出池
的时候,把p1释放掉。第四⾏行代码⼜又创建了⼀一个autoreleasepool。第五⾏行代码再次向p1
发送了autorelease消息。当代码执⾏行到第六⾏行的“}”时,第⼆二个⾃自动释放池结束,这时p1
引⽤用技术减1,p1所指向的内存(对象)的retainCount由1变为0,内存被系统回收。代码
执⾏行到第七⾏行时,最外层的⾃自动释放池结束,再次向p1发送release消息,出现过度释
放。
@autoreleasepool {
[p1 autorelease];//此时p1被加⼊入⾃自动释放池1
@autoreleasepool {
[p1 autorelease];//此时p1被加⼊入⾃自动释放池2
}//此处,⾃自动释放池2结束,p1引⽤用计数-1
}//此处,⾃自动释放池1结束,但是向已经被释放的对象p1发送了消息
3、ARC下dealloc⽅方法存在的意义在于什么地⽅方?举例说明⼀一下具体的使⽤用场景。
答:其实在MRC中dealloc⽅方法存在的主要意义是为了释放⾃自⾝身的实例变量,移除观察者,停⽌止
timer,移除通知,代理置空等。ARC下,系统会帮助我们释放该对象所包含的实例变量,但是有
些对象还是需要们⾃自⼰己去释放的(⽐比如Core Foundation框架下的⼀一些对象),另外通知中观察者
的移除,代理置空,停⽌止timer等
⽰示例如下所⽰示:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];//
移除通知观察者
[[XMPPManager sharedManager]
removeFromDelegateQueue:self];//移除委托引⽤用
[[MyClass shareInstance] doSomething ]//其他操作
scrollView.delegate = nil;
[timer invalidate];
}
4、分别写出MRC中在assign、retain、copy下属性name对应的setter⽅方法的内部实现。
答:
assign下
- (void) setName:(NSString*)name
{
_name = name;
}
retain下
- (void) setName:(NSString*)name
"2
2015年11⽉月9⽇日 星期⼀一
{
if(_name != name){
[_name release];
_name = [name retain];
}
}
copy下
- (void) setName:(NSString*)name
{
if(_name != name){
[_name release];
_name = [name copy];
}
}
5、在Category中本⾝身不允许为已有的类添加新的属性或者成员变量,你有没有其他的⽅方法可以在
category中添加属性或者是成员变量?
答:⼀一种⽅方法就是使⽤用runtime.h中的objc_getAssociatedObject和objc_setAssociatedObject来访
问和⽣生成关联对象。例如为NSObject添加⼀一个类⺫⽬目,分类中添加⼀一个属性。代码如下所⽰示:
NSObject+Test.h⽂文件
#import <Foundation/Foundation.h>
@interface NSObject (Test)
@property (nonatomic, strong) NSString *test;
@end
NSObject+Test.m⽂文件
#import “NSObject+Test.h"
#import <objc/runtime.h>
static const void *instanceNameKey = &instanceNameKey;
@implementation NSObject (Test)
@dynamic test;
- (NSString *)test
{
return objc_getAssociatedObject(self, instanceNameKey);
}
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, instanceNameKey, test,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
6、@synthesize和@dynamic有什么区别?
(1)@property有两个对应的词,⼀一个是@synthesize,⼀一个是@dynamic。如果@synthesize和
@dynamic都没写,那么默认的就是@syntheszie var = _var;
(2)@synthesize的语义是如果你没有⼿手动实现setter⽅方法和getter⽅方法,那么编译器会⾃自动为你加
上这两个⽅方法。
(3)@dynamic告诉编译器:属性的setter与getter⽅方法由⽤用户⾃自⼰己实现,不⾃自动⽣生成。(当然对于
readonly的属性只需提供getter即可)。假如⼀一个属性被声明为@dynamic var,然后你没有提供
"3
2015年11⽉月9⽇日 星期⼀一
@setter⽅方法和@getter⽅方法,编译的时候没问题,但是当程序运⾏行到instance.var = someVar,由
于缺setter⽅方法会导致程序崩溃;或者当运⾏行到 someVar = instance.var时,由于缺getter⽅方法同样
会导致崩溃。编译时没问题,运⾏行时才执⾏行相应的⽅方法,这就是所谓的动态绑定。@dynamic可⽤用
于在分类中添加属性(需要⽤用到objc_getAssociatedObject和objc_setAssociatedObject函数)。
7、你如何看待iOS中的拷⻉贝?
答:在我看来,⽇日常⽣生活中,当我们⽤用到”拷⻉贝”这个词语的时候,不管怎样都会产⽣生两
份。⼀一份是原来的,⼀一份是新拷⻉贝出来的。但是到⺫⽬目前为⽌止,在iOS中我看到了三种拷⻉贝
⽅方式:
(1)伪拷⻉贝:伪拷⻉贝,顾名思义,就是假拷⻉贝,没有拷⻉贝出新的对象。这⼀一点对于NSString
这种类簇来说⽐比较常⻅见,NSString本⾝身是不可变字符串,内容不可能被修改,因此我们也
没有拷⻉贝的必要,因为拷⻉贝的⺫⽬目的是防⽌止原件被修改,所以才拷⻉贝出来⼀一份,伪拷⻉贝实际
上是对象引⽤用计数加了1(相当于retain或者strong的功效)。
(2)浅拷⻉贝:浅拷⻉贝就是确实拷⻉贝出来⼀一份和原来内容⼀一样的新对象。但是对于对象⾃自带的
属性是伪拷⻉贝,两个对象的属性指向同⼀一个内存。
(3)深拷⻉贝:深拷⻉贝就是不仅仅拷⻉贝出⼀一份新对象,⽽而且对象的属性也拷⻉贝了⼀一份。
总的来说,如果在开发的过程中需要实现拷⻉贝,那么需要接受NSCopying协议。实现
copyWithZone:⽅方法。浅拷⻉贝、深拷⻉贝的区别在于copyWithZone:⽅方法的实现不同。
8、⽗父类实现深拷⻉贝时,⼦子类如何实现深拷⻉贝 ?⽗父类没有实现深拷⻉贝时,⼦子类如何实现深
度拷⻉贝?
答:⽗父类实现深拷⻉贝之后,⼦子类在重写的copyWithZone⽅方法,先调⽤用⽗父类的
copyWithZone⽅方法,之后实现⾃自⼰己属性的拷⻉贝。
如果⽗父类没有实现深拷⻉贝,⼦子类除了需要对⾃自⼰己的属性进⾏行拷⻉贝,还要对⽗父类的属性进⾏行
拷⻉贝。
9、系统中有哪些单例类?在真实的开发中,单例的应⽤用场景在于什么地⽅方?
答:系统为我们提供了很多单例类。例如:UIScreen、UIDevice、NSFileManager、
NSNotificationCenter,NSUserDefaults,UIApplication等等。⽽而且在实际的开发中除了
使⽤用系统给我们提供的单例类之外,还会根据需求⾃自⼰己创建单例类。例如:1.数据库处
理。对于数据的增删改查,可能在很多界⾯面都会⽤用到,那这个时候,可以定义⼀一个针对数
据处理的单例类。2.⾳音乐播放。在做⾳音乐播放类项⺫⽬目时,⼀一般会在多个⻚页⾯面都可以播放⾳音
乐,这时也可以讲播放器定义为单例类,在多个界⾯面使⽤用。3.下载管理,当下载⾳音乐或者
视频时,应该定义⼀一个单例类管理下载任务。4.登录类程序⾥里⾯面,还可以定义单例类保存
⽤用户资料,状态等。
10、写出在多线程情况下的⼀一个单例。
答:⼀一般情况下,在开发中我们所写的单例都是伪单例。即只是保证了在调⽤用某⼀一⽅方法时,
所产⽣生的对象只有⼀一个,但是没有考虑其他的影响其引⽤用计数的⽅方法。例如retain、copy
等。为了保证线程安全,单例的写法如下所⽰示:
"4
2015年11⽉月9⽇日 星期⼀一
⽅方法⼀一:
static AccountManager *sharedAccountManagerInstance = nil;
+ (AccountManager *)sharedManager{
@synchronized (self){
if (sharedAccountManagerInstance == nil) {
sharedAccountManagerInstance = [[AccountManager alloc] init];
}
}
return sharedAccountManagerInstance;
}
⽅方法⼆二:
static AccountManager *sharedAccountManagerInstance = nil;
+ (AccountManager *)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedAccountManagerInstance = [[AccountManager alloc] init];
});
return sharedAccountManagerInstance;
}
11、写⼀一个标准的宏,这个宏输⼊入两个参数并且返回较⼩小的⼀一个。
答:#define Min(X,Y) ((X) < (Y) ?(X):(Y))
12、应⽤用程序在启动的时候主要做了什么操作?
答:应⽤用程序在启动的时候,会执⾏行main函数,⽽而main函数⾥里⾯面主要执⾏行了
UIApplicationMain函数。UIApplicationMain函数执⾏行完主要做了以下三个操作:
(1)创建应⽤用程序UIApplication对象。
(2)创建了应⽤用程序代理对象。默认的应⽤用程序代理对象是AppDelegate。
"5
2015年11⽉月9⽇日 星期⼀一
(3)建⽴立⼀一个事件循环RunLoop。⽤用来实时监测应⽤用程序中的各种事件(触摸,晃动,远
程控制事件,通知,观察者,timer等等)。
13、简述应⽤用程序按Home键进⼊入后台时的⽣生命周期,以及从后台回到前台时的⽣生命周
期。
答:正在运⾏行的程序,点击了Home键之后,应⽤用程序会由活跃状态变为⾮非活跃状态,之
后应⽤用程序进⼊入后台。在这个过程中执⾏行的⽅方法分别是applicationWillResignActive:和
applicationDidEnterBackground:。
当应⽤用程序从后台回到前台时,应⽤用程序会先进⼊入前端,然后由⾮非活跃状态变为活跃
状态。在这个过程中执⾏行的⽅方法分别是:
applicationWillEnterForeground:和applicationDidBecomeActive:
14、ViewController 的 alloc,loadView,viewDidLoad,dealloc在⾃自定义ViewController
中这⼏几个函数⾥里⾯面应该做什么⼯工作?
答: alloc⽅方法⼀一般情况下会直接调⽤用,主要是为了开辟内存空间,创建对象。
重写loadView⽅方法的主要⺫⽬目的是使⽤用⾃自⼰己创建的view(⼀一般会定义⼀一个UIView⼦子
类,⽤用⼦子类创建对象,⼦子类中已经添加好对应的控件了)作为ViewController的View,⽽而
不是直接使⽤用系统⾃自带的View。loadView⽅方法和我们在storyboard或XIB中拖控件是等价
的。
viewDidLoad⽅方法⼀一般情况下会给添加好的控件赋值(⽐比如⻚页⾯面传值时,列表⻚页⾯面
会把model传递给detail⻚页⾯面,在detail⻚页⾯面的viewDidLoad中使⽤用model中的数据给控件赋
值),开启timer,⺴⽹网络请求等。
dealloc⽅方法在执⾏行的时候,主要是释放控制器的实例变量或者是将delegate置空、
移除观察者,停⽌止timer等。
15、请描述⼀一下viewController⼏几个重要⽅方法的执⾏行时机
答:视图控制器的⽣生命周期分为创建、 显⽰示 和 销毁。
(1)创建
xxx *x = [[xxx alloc] init] // 作⽤用:分配内存空间,创建控制器时使⽤用
(2)加载和显⽰示:
- (void)loadView: // 作⽤用:加载根视图(即为self.view赋值) ,在控制器view属性的getter
⽅方法⾸首次调⽤用的时候执⾏行此⽅方法。
根视图已加载:
- (void)viewDidLoad// loadView执⾏行完毕之后,⽴立即执⾏行此⽅方法。作⽤用:给创建好的控件
设置值,做⺴⽹网络请求,开启timer等
"6
2015年11⽉月9⽇日 星期⼀一
loadView 和 viewDidLoad 当⾸首次访问controller的根视图时才会执⾏行,即view属性的getter
⽅方法⾸首次使⽤用时执⾏行, loadView 执⾏行在前, viewDidLoad执⾏行在后。
如果重写了 loadView ⼀一定要给控制器的根视图赋值(self.view = xxView;),在给控制
器的根视图赋值的之前,不能使⽤用控制器view属性的getter⽅方法,否则会重复执⾏行
loadView 和 viewDidLoad ⽅方法。
视图将要显⽰示:
- (void)viewWillAppear:
视图将要被添加到 UI 层级上,还没有添加时执⾏行。(此时视图还没有显⽰示)
视图已经显⽰示:
- (void)viewDidAppear
视图已经被添加到 UI 层级上,视图已经显⽰示出来时执⾏行。(视图已经显⽰示)
视图将要消失:
- (void)viewWillDisappear:
视图将要被从 UI 层级上⾯面移除,但视图还没有移除时执⾏行。 (视图还可⻅见)
视图已经消失:
- (void)viewDidDisappear:
视图已经从 UI 层级上⾯面移除,视图已经不可⻅见时执⾏行。 (视图不可⻅见)
(3)销毁:
- (void)viewWillUnload
iOS6.0之后已经被弃⽤用。iOS 5.0 之前,当低内存且控制器的view不需要使⽤用的时候会调
⽤用这个⽅方法,即当控制器的根视图 将要 被释放时执⾏行([vc.view release],vc.view还未置
空),我们可以在这个⽅方法中移除⼀一些跟视图相关的观察者和通知,并记录视图的状态,
以便之后重新创建视图。 iOS6.0 之后不再需要做释放了,该⽅方法也被遗弃了。
- (void)viewDidUnload
iOS6.0 之后已经被弃⽤用。iOS 5.0 之前,当低内存且控制器的view不需要使⽤用的时候会调
⽤用这个⽅方法,即当控制器的根视图被释放时执⾏行([vc.view release],vc.view已经为
nil),这个⽅方法给我们⼀一个机会做内存的相关清理⼯工作,如果控制器对某些视图有引⽤用,
可以在这⾥里释放这些引⽤用,同样可以释放⼀一些懒加载的对象,但是不要释放那些不容易重
新加载的数据。 iOS6.0 之后不再需要做释放了,该⽅方法也被遗弃了。
16.在使⽤用系统的⼀一些类例如UITableView时,会发现其delegate属性的语义设置为assign
⽽而不是retain,为什么呢?
"7
2015年11⽉月9⽇日 星期⼀一
答:delegate 的语义修饰符 为assign 不是 retain 主要是为了避免两个对象因相互引⽤用造
成的内存泄露问题。
因为 retain修饰delegate,当通过set⽅方法赋值时,对象的引⽤用计数会+1.
如果此时两个对象具有如下关系:A对象有⼀一个属性是B,B的delegate是A。即互相为彼
此的属性。例如: A 是viewController ,B是tableView. B 是A的属性,B的引⽤用计数会
+1,B的delegate是A,A 的引⽤用计数也会 +1. 那么如果A没有释放,B就⼀一直不会释放,
因为A对B有拥有权。 同理,B没有释放,A也⼀一直释放不掉,因为B对A也有拥有权。 因
此A 和 B 的dealloc 都会不执⾏行,造成内存泄露.
代码演⽰示如下(只写出了核⼼心代码):
A *a = [[A alloc] init] // a 引⽤用计数:1
B *b = [[B alloc] init] // b 引⽤用计数:1
a.b = b; // b 引⽤用计数: 2
b.delegate = a; // a 引⽤用计数: 2
[a release]; // a引⽤用计数: 1
[b release]; // b引⽤用计数: 1
// 最终 A 和 B 都释放不掉。
assign则是直接赋值,并不会引起引⽤用计数增加,因此使⽤用assign 不会出现内存泄露。
代码演⽰示如下(只写出了核⼼心代码):
A *a = [[A alloc] init] // a 引⽤用计数:1
B *b = [[B alloc] init] // b 引⽤用计数:1
a.b = b; // b 引⽤用计数: 2
b.delegate = a; // a 引⽤用计数: 1
[a release]; // a引⽤用计数: 0, b引⽤用计数:1(在a的dealloc⾥里⾯面_b被释放了)
[b release]; // b引⽤用计数: 0
// 最终 A 和 B 都可以释放掉。
17.UIImage初始化⼀一张图⽚片的⽅方法以及优缺点
答:(1)UIImage *image = [UIImage imageNamed:@”1.png”];
这个⽅方法创建的图⽚片是从缓存⾥里⾯面获取的, 先在缓存⾥里⾯面查,看是不是有这个图⽚片, 没有的
话将图⽚片添加进缓存再使⽤用. 有的话直接使⽤用缓存⾥里⾯面的. 在程序中,如果这张图⽚片要在多
"8
2015年11⽉月9⽇日 星期⼀一
个地⽅方使⽤用的话, 建议使⽤用这种⽅方式. 缺点是⼀一旦加⼊入缓存,就⼀一直占⽤用内存,不能被释放
掉。
(2)//读取本地图⽚片路径
NSString *imagePath=[NSString stringWithFormat:@"%@/Documents/
%@.jpg",NSHomeDirectory(),@"test"];
[UIImage imageWithContentsOfFile:imagePath];
从⼿手机本地读取, ⽐比较第⼀一种⽅方式, 这个是直接加载图⽚片的,图⽚片不需要的时候,可以
release掉. 所以建议在使⽤用重复率低的图⽚片时使⽤用这个⽅方法.
(3) // 以下这种⽅方式会卡线程,建议代码放到⼦子线程⾥里⾯面。
// imageWithData: data可以通过data创建图⽚片(data可以来⾃自⺴⽹网络也可以来⾃自本地)
NSURL *url = [NSURL URLWithString:@“http://e.picphotos.baidu.com/album/s
%3D550%3Bq%3D90%3Bc%3Dxiangce%2C100%2C100/
sign=f51d2708cb11728b342d8c27f8c7b2f3/
bba1cd11728b47109d0f5555c5cec3fdfc032302.jpg?
referer=f55e30e2d71373f0ac285aaf8d00&x=.jpg"];
UIImage *image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
需要注意的是,如果imageWithData是同步⺴⽹网络请求,如果在主线程中直接使⽤用,会卡主
线程,因此⼀一般不会在主线程中直接使⽤用,⽽而是采⽤用异步⺴⽹网络请求获取data赋值。
18.当⼿手指点击屏幕上的登录按钮时,响应者链的检测过程是什么样⼦子的?
答:当⼿手指触摸到屏幕上的登录按钮时,⾸首先runloop会检测到事件,将事件传递给
UIApplication对象,UIApplication对象将事件传递给AppDelegate对象,之后
AppDelegate对象将事件传递给window对象,然后传递给rootViewController,之后是view,
之后检测View上的⼦子视图,通过⽐比对触摸的位置定位到对应的Button。
19.在⼀一个imageView上添加Button,给Button添加⼀一个事件,点击Button时能否响应事
件?如果不能,⽤用响应者链解释⼀一下为什么不可以?
答:不能。因为imageView的⽤用户交互默认是关闭的。imageView的⽤用户交互关闭,阻断
了响应者链的检测过程,所以在检测的过程中,就直接不检测imageView以及它上⾯面的其
他控件,也就检测不到button的存在。因为响应的过程正好是和检测的过程相反,因为检
测不到button被触摸,所以button的点击事件也不会响应。解决办法就是把imageView的
⽤用户交互打开。
20.列举⼀一下常⽤用的第三⽅方框架
答:我在做开发的过程中曾经⽤用过以下第三⽅方框架:
(1)AFNetworking ⺴⽹网络请求库
(2)SDWebImage ⺴⽹网络图⽚片加载
"9
2015年11⽉月9⽇日 星期⼀一
(3)FMDB 数据库
(4)Masonry ⾃自动布局
(5)UmengSDK/Share SDK 分享
(6)融云/环信 即时通信
(7)ZBar/ZXing ⼆二维码扫描和⽣生成
(8)MBProgressHUD ⻛风⽕火轮
(9)GTMBase64 base64编码解码
(10)MJRefresh 下拉刷新,上拉加载
(11)百度/⾼高德地图SDK
21.MJRefresh 原理
答:给UISCrollView添加了分类,在分类中,根据scrollView的contentOffset进⾏行判断⽅方向和偏移
量,当偏移量到某⼀一个临界值的时候,开始切换header或者footer的状态,以呈现不同的UI,当偏
移量⼤大于临界值并松开⼿手的时候, 执⾏行block回调或者target..action的⽅方法, 主要在block或者
target..action中进⾏行⺴⽹网络请求,请求结束后,调⽤用endRefresh⽅方法,再次更改header或者footer的
状态,使他们回到初始状态。
22.block在定义成属性时应该⽤用什么关键字,为什么?
答:block在定义成属性时关键字应该使⽤用copy。我们平时使⽤用的block,主要是存放在栈
区的(有的是在全局区),栈区的block出了作⽤用域就会被释放,当我们进⾏行block回调的
时候,block已经被系统给销毁了,会出现crash。为了管理和使⽤用block,需要将其拷⻉贝到
堆区。全局区的block理论上是不⽤用拷⻉贝的,但拷⻉贝也没什么坏处,不⽤用的时候,我们也
会对copy的对象进⾏行释放,不会造成内存泄露。因为block在栈区的情况⽐比较多,为了书
写⽅方便,保持格统⼀一,我们把block定义成属性时,使⽤用copy关键字。
23.定义⼀一个返回值为字符串类型,参数是int类型的Block。并且调⽤用该Block。
答:Block定义如下所⽰示:
NSString * (^block)(int a) = ^ NSString * (int a){
return [NSString stringWithFormat:“%d”,a];
};
Block调⽤用:
NSString *str = block(10);
24. 请谈谈你对block和delegate模式认识?
答:⽆无论是block还是delegate模式本质上都是回调,使⽤用block,其优点是回调的block代
码块直接就放在了block赋值的地⽅方,使代码更为紧凑,缺点是block内使⽤用到当前类的实
例变量的时候,需要注意循环引⽤用的问题,即需要使⽤用__block(MRC下)或者
__weak(ARC下)定义⼀一个弱引⽤用的self出来,block⾥里⾯面使⽤用弱引⽤用的self去操作属性或调
⽤用⽅方法。delegate模式不⽤用像block⼀一样做特殊处理,但是如果多个对象设置的代理是同⼀一
个对象,就需要在delegate⽅方法中判断当前执⾏行代理的是哪个对象。
25.什么是沙盒?
"10
2015年11⽉月9⽇日 星期⼀一
答:所谓的沙盒其实是操作系统为应⽤用程序分配的⼀一个密闭⽂文件夹。应⽤用程序拥有这个⽂文
件夹内⽂文件的访问权限,且只能对这个⽂文件夹内的⽂文件进⾏行操作(当然也可以访问系统提
供的⽂文件,⽐比如:相册),不可以去访问其他应⽤用程序的⽂文件夹。
26.在沙盒中有⼏几个⽂文件夹?
答:沙盒含有3个⽂文件夹:Documents, Library 和 tmp
Documents:苹果建议将程序中建⽴立的或在程序中浏览到的⽂文件数据保存在该⺫⽬目录下,
iTunes备份和恢复的时候会包括此⺫⽬目录。
Library:存储程序的默认设置或其它状态信息。
iTunes在与iPhone同步时,备份所有的Documents和Library⽂文件。
Library/Caches:存放缓存⽂文件,⼀一般是下载的图⽚片和视频,iTunes不会备份此⺫⽬目录,此
⺫⽬目录下⽂文件不会在应⽤用退出删除。
tmp:临时⽂文件夹,应⽤用程序在重启时,会丢弃所有的tmp⽂文件。
27.你熟悉的设计模式有哪些,请说说对他们的理解。
答:单例,通知,KVO,代理,target-action等
单例,主要特点是⼀一个类只有⼀一个对象。对外提供了⼀一个获取唯⼀一对象的⽅方法,⼀一般都是
类⽅方法,完整的单例会重写很多引⽤用计数相关的⽅方法(⽐比如:allocWithZone,
copyWithZone,retain,release,autorelease,retainCount等)以保证对象在任何情况
下都唯⼀一。单例最⼤大的价值就是建⽴立程序级别的全局变量,就是把不同模块都要⽤用的变量
以属性的形式放到单例⾥里⾯面,以便随时使⽤用。⾳音频播放类程序⼀一般会写单例来管理需要播
放的⾳音乐信息,需要下载的地⽅方也会以单例的形式来管理下载对象。
通知,是M与C通信的⽅方式之⼀一。⼀一般是Model发送变化的时候,会发送通知告诉
Controller,Controller再做相应的处理。需要先往通知中⼼心⾥里⾯面注册观察者,然后在合适
的时机,通知中⼼心post通知,观察者做对应的处理,当观察者将要释放的时候,从通知中
⼼心移除观察者。
KVO,也是M与C通讯的⽅方式。⼀一般是C去观察M的某个属性,某个属性发⽣生变化之后,C
做出相应的处理,当C将要释放的时候,M移除观察者。
代理,是V与C通信的⽅方式之⼀一。⼀一般C是V的代理,V发⽣生变化的时候,C做对应的调整。
例如:UITableView被点击了,由Controller做处理。我们在⾃自⼰己写代理的时候,⼀一定要清
楚谁来处理事件,谁来调⽤用代理⽅方法。通常情况下都是C处理时间,V在适当的时候调⽤用
代理⽅方法。
target-action,也是V与C通信的⽅方式之⼀一。⼀一般C是V的target,V被点击或者被滑动之
后,C做出对应的处理。⽆无论是target-action还是代理,核⼼心都是回调。
28.NSNotification和KVO的区别和⽤用法是什么?什么时候应该使⽤用通知,什么时候应该使⽤用
KVO,它们的实现上有什么区别?
"11
2015年11⽉月9⽇日 星期⼀一
答:通知和KVO都是观察者模式的体现,⼆二者侧重点有些不同。
通知往往⽤用于1对多的场景,多个观察者观察⼀一个通知,⼀一旦这个通知被通知中⼼心post,
所有观察者都可以做出响应。具体的实现流程是:(1)通知中⼼心通过addObserver⽅方法添加
观察者,其实是把观察者和通知进⾏行绑定,多次使⽤用addObserver⽅方法可以为同⼀一个通知
添加多个观察者。(2)通知中⼼心发送通知。(3)各个观察者做各⾃自的处理。(4)在观察者销毁
之前(dealloc中),从通知中⼼心移除观察者。
KVO多⽤用于1对1的场景,⼀一个对象去观察另外⼀一个对象的属性值有没有发⽣生变化,⼀一旦发
⽣生变化,观察者做出响应。具体的流程是:(1)被观察者添加观察者,指定观察者观察的属
性。(2)被观察者的属性在某⼀一时间发⽣生了变化。(3)观察者做出响应。(4)在观察者销毁之
前,先移除观察者。
KVO其实也可以1对多,就是多个观察者观察同⼀一个对象同⼀一个属性的变化。KVO和通知
给⼈人的感觉⼀一个主动通知(通知会由通知中⼼心主动post),⼀一个是被动通知(KVO,观察
者⼀一直盯着属性变没变,⼀一旦变化,⾃自⼰己就做出响应。被观察的对象不是主动告知你我变
了)。
29.定义属性时,什么情况使⽤用copy,assign,和retain?
答:assign⽤用于简单数据类型,如NSInteger,double,bool。retain 和copy⽤用于对象类型,
⼆二者⼜又有区别,copy是⽤用于⼀一个对象有可能被修改,但不想修改原件,所以拷⻉贝⼀一份出来
(新拷⻉贝的对象引⽤用计数为1),这样新拷⻉贝的修改了,原件不会变,原件修改了,新拷
⻉贝的不会变。⽽而retain呢,对象引⽤用计数+1,对象只有⼀一个,但引⽤用计数为2,通过任何⼀一
个引⽤用修改对象内容,另外⼀一个引⽤用使⽤用的时候,⽤用的就是修改之后的内容。
30.在项⺫⽬目中什么时候选择使⽤用GCD,什么时候选择使⽤用NSOperation?
答:⽆无论是GCD还是NSOperation其实都是多线程的⼀一种实现形式。严格的说
NSOperation和线程并没有必然联系,更不是多线程,NSOperation只是操作,封装了
target和action 或者Block,在主线程中执⾏行Operation,Operation就会运⾏行在主线程中,
在⼦子线程中执⾏行Operation,Operation就运⾏行在⼦子线程中。它只有和NSOperationQueue
联合使⽤用的时候,才能发挥出价值。NSOperationQueue是Operation的管理者,它⾸首先是
⼀一个队列,先来的任务先开始执⾏行,后来的任务后开始执⾏行,这些任务的执⾏行是并发的,
NSOperationQueue负责为任务开辟线程以及关闭线程,有点类似于cell重⽤用,⽤用有限的
线程执⾏行任意数量的任务,同时可以设置最⼤大并发数,控制程序的性能,不⾄至于全部任务
⼀一起执⾏行。GCD出现⽐比NSOperationQueue要晚,是⼀一套基于C函数的API,由于是C函
数,所以性能⽐比较⾼高,使⽤用灵活,安全,当然功能也强⼤大。但是相对于GCD来讲,
NSOperationQueue对线程做了封装,使⽤用⽐比较简单,尤其是不⽤用管理线程的开启和关
闭。个⼈人认为,如果仅仅是想要实现异步执⾏行任务,⾸首选GCD,如果要管理多个异步任
务,NSoperationQueue⽐比较⽅方便。如果要做更复杂的处理,⽐比如前5个任务并发,5个任
务执⾏行完之后,需要串⾏行执⾏行3个任务,之后再并发执⾏行若干任务,就需要使⽤用GCD了。
"12
2015年11⽉月9⽇日 星期⼀一
总的来说,特别简单的和⾮非常复杂的多线程任务GCD⽐比较适合,介于⼆二者之间的⽤用
NSOperationQueue⽐比较合适。
31.tableView必须实现的两个⽅方法是什么(⼿手写)?在哪个协议中声明的?
答:在UITableViewDataSource协议中声明的两个必须实现的⽅方法
1.-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
2.-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
32.UITableViewController 中,创建UITableViewCell时,initWithSytle:resuseIdentifier:
中,reuseIdentifier有什么⽤用?简述UITableViewCell的复⽤用原理.
答:只有在cell被滑动出界⾯面的时候,此cell才会被加⼊入到复⽤用队列中。每次在创建cell的
时候,程序会⾸首先通过调⽤用dequeueReusableCellWithIdentifier:cellType⽅方法,到复⽤用队
列中去寻找标⽰示符为“cellType”的cell,如果找不到,返回nil,然后程序去通过调⽤用
[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]来创建
标⽰示符为“cellType”的cell。
33.#include、#import、@class这三个关键字是什么指令?
答:是预编译指令。所谓的预编译指令指的就是在编译之前,先去执⾏行的指令。
34.在oc中引⼊入头⽂文件使⽤用的关键字是哪⼀一个?能在c语⾔言⽂文件中使⽤用吗?
答: #import关键字可以在OC中使⽤用,不能在C⽂文件中使⽤用, #include可以在C和OC中使
⽤用。
35.#import与#include相⽐比,好处是什么?
答: #import确定⼀一个⽂文件只能被导⼊入⼀一次,避免的重复导⼊入的问题,使⽤用#include⼀一定
要注意重复导⼊入的问题。所以在OC中都使⽤用#import来引⽤用头⽂文件。
36.#import<>和#import””的区别是什么?
答:#import<>⽤用于对系统⽂文件的引⽤用,编译器会在系统⽂文件⺫⽬目录中去查找⽂文件
#import””⽤用于对⾃自定义的⽂文件的引⽤用,编译器⾸首先回去⽤用户⺫⽬目录下查找,然后去安装⺫⽬目
录,最后去系统⺫⽬目录中查找⽂文件。
37.@class的作⽤用是什么?
答:@class的作⽤用是告诉编译器有@class后⾯面的内容是⼀一个类名。只是告诉编译器存在
这么⼀一个类,类具体包含哪些⽅方法,属性和变量的并没有告诉编译器。⼀一般在类的头⽂文件
中使⽤用@class来引⼊入其他类。
"13
2015年11⽉月9⽇日 星期⼀一
38.多线程的优点和缺点分别是什么?
答:优点:1、将耗时较⻓长的操作(⺴⽹网络请求、图⽚片下载、⾳音频下载、数据库访问等)放
在⼦子线程中执⾏行,可以防⽌止主线程的卡死;2、可以发挥多核处理的优势,提升cpu的使
⽤用率。
缺点:1、每开辟⼀一个⼦子线程就消耗⼀一定的资源; 2、会造成代码的可读性变差;3、
如果出现多个线程同时访问⼀一个资源,会出现资源争夺的情况
39.NSOperationQueue中有⼀一个属性叫maxConcurrentCount即最⼤大并发数。这⾥里所谓的
最⼤大并发数指的是什么含义?
答:这⾥里的最⼤大并发数指得是在队列中同时执⾏行的任务的个数。有很多⼈人会认为是所分配
的线程的个数。其实不是的。因为线程的个数的多少取决于系统,系统会分配合适的线程
数量来保证这些任务并发执⾏行的,作为程序员我们是没办法控制的。
40.请写出下⾯面代码的打印结果:
NSLog(@"1”);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3”);
答:只能打印1.第⼀一⾏行代码按照顺序会打印1.当执⾏行到第⼆二段代码的时候出现了问题。因
为在主线程中使⽤用了同步,这时为了执⾏行block内部的代码需要阻塞当前线程去执⾏行
Block,但是由于block也是在主线程⾥里⾯面执⾏行,主线程当前还有任务没执⾏行完,产⽣生主线
程死锁现象。代码没办法继续执⾏行。
41.项⺫⽬目中,在什么情况下会⽤用到多线程?
答:多线程处理包括Core Data的多线程访问,耗时的数据计算,数据库访问,异步⺴⽹网络
请求以及⼀一些在运⾏行态内存吃紧的情况下处理⼤大⽂文件的⽅方案等。
42.iOS中哪些数据持久化的⽅方式,各有什么特点,iOS平台怎么做数据的持久化,CoreData和
sqlite有⽆无必然联系?CoreData是⼀一个关系型数据库吗?
答:iOS中可以有四种持久化数据的⽅方式: 属性列表、对象归档、SQLite3和Core Data;
Core Data可以使你以图形界⾯面的⽅方式快速的定义app的数据模型,同时在你的代码中容易
获取到它。core data提供了基础结构去处理常⽤用的功能,例如保存,恢复,撤销和重做,
允许你在app中继续创建新的任务。在使⽤用Core Data的时候,你不⽤用安装额外的数据库系
统,因为core data使⽤用内置的sqlite数据库。Core Data将你app的模型层放⼊入到⼀一组定义
在内存中的数据对象中。Core Data会追踪这些对象的改变,同时可以根据需要做相反的
"14
2015年11⽉月9⽇日 星期⼀一
改变,例如⽤用户执⾏行撤销命令。当Core Data在对你app数据的改变进⾏行保存的时候,
Core Data会把这些数据归档,并永久性保存。
mac os X中sqlite库,它是⼀一个轻量级功能强⼤大的关系数据引擎,也很容易嵌⼊入到应⽤用程
序。可以在多个平台使⽤用,sqlite是⼀一个轻量级的嵌⼊入式sql数据库编程。与core data框架
不同的是,sqlite是使⽤用程序式的,sql的主要的API来直接操作数据表。
Core Data不是⼀一个关系型数据库,也不是关系型数据库管理系统(RDBMS)。虽然Core
Dta⽀支持SQLite作为⼀一种存储类型,但它不能使⽤用任意的SQLite数据库。Core Data在使⽤用
的过程中⾃自⼰己创建这个数据库。Core Data⽀支持对⼀一、对多的关系。
43.id声明的对象有什么特性?
答:id是任意对象类型的,不能表⽰示基本类型。id类型是通⽤用指针类型,因为通过指针,也
就是内存地址来引⽤用对象,所以可以将任意对象赋值给id类型的对象。返回id类型值的⽅方
法是返回指向内存中某对象的指针。然后可以将该值赋给任何对象变量(强制类型转换即
可)。因为⽆无论在哪⾥里,对象总是携带它的isa成员。所以即使将它存储在id类型的通⽤用对
象变量中,也总是可以确定它的真实类型,id是多态的⼀一种体现。
44.对于语句NSString* testObject = [[NSData alloc]init];testObject在编译时和运⾏行时分别
是什么类型的对象?
答:编译时是NSString,运⾏行时是NSData。
45.什么是懒加载?在使⽤用懒加载时的注意事项是什么?
答:所谓的懒加载指的是延迟创建对象,只有当需要的时候才创建对象。在真正开发的过
程中其实懒加载就是重写getter⽅方法。在getter⽅方法的内部,实现对象的创建,如果对象为
nil才创建,如果不为nil,直接返回对象。在真正使⽤用懒加载时需要注意的是当第⼀一次使⽤用
对象时,需要调⽤用self.因为只有这样才能调⽤用对应的getter⽅方法,对象才会被创建。
46.谈谈你对RunLoop的理解。
答:⼀一个程序从main函数开始,函数执⾏行完毕之后就会退出,iOS程序也是⼀一样的,但是
我们从没看到过iOS程序打开之后直接闪退,肯定是有⼀一些东⻄西阻⽌止了程序的退出,最简
单的就是添加⼀一个死循环,RunLoop就是类似于这样的⼀一个死循环,保证你的应⽤用程序不
被退出,区别就是RunLoop会在你的程序有事件(点击事件、摇晃事件等)要处理的时候
才会去让cpu处理,在程序没有事件处理的时候就让系统cpu休眠。在iOS中,每个线程都
有⼀一个RunLoop,但是默认状态下只有主线程的RunLoop是开启的(系统⾃自动帮我们开
启),其他线程开启需要以下代码
BOOL isRunning = NO;
while (!isRunning) {
"15
2015年11⽉月9⽇日 星期⼀一
isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
currentRunLoop 可以获取当前线程的RunLoop , 循环是为了保证能开启RunLoop(系统
繁忙时有可能开启失败)
如果⽤用timerWithTimeInterval来创建NSTimer,我们需要把这个Timer假如到RunLoop才能
执⾏行,如果是在⼦子线程,还需要开启这个RunLoop。
47.简单描述⼀一下RunLoop在实际开发中的应⽤用场景。
答:在做项⺫⽬目时曾遇到过两个问题,需要⼿手动开启runLoop:
1.在⼀一个⻚页⾯面中有⼀一个轮播图和tableView。在滑动tableView的过程中,轮播图不动了。
这时我是⽤用了RunLoop将轮播图的NSTimer加⼊入到了RunLoop中。
2.在⼦子线程中开启了另外⼀一个⼦子线程⽤用于下载图⽚片。这时发现下载图⽚片的代码不执⾏行。通
过在下载图⽚片的代码⾥里⼿手动开启RunLoop,代码才可以执⾏行。
48.什么是Runtime
答:Runtime就是运⾏行时,⼀一个程序开发的过程通常可以分为以下阶段,编辑-预编译-编
译-连接-运⾏行,运⾏行时可以说就是我们的程序再运⾏行的阶段发⽣生的⼀一些事情,在这个阶段
程序通常会把⼀一些OC的代码转化成C语⾔言的代码,从⽽而提⾼高执⾏行的效率,在这个阶段我们
也可以动态的为某个对象的属性赋值,⽽而对象的属性具体是什么类型也会在这个阶段进⾏行
确定(NSString *str = [NSData data]; 其中str在编译的时候是NSString类型,运⾏行的时候
是NSData类型)。系统也提供了Runtime的类库,让我们可以直接调⽤用⼀一些运⾏行时把OC
代码转化C之后的代码⽐比如:objc_msgSend();同样也可以通过运⾏行时,为分类添加属
性,需要⽤用到objc_getAssociatedObject和objc_setAssociatedObject函数。
49.解释这句代码的意思objc_msgSend(array,@selector(insertObject:atIndex:), foo, 5);
答:给array 发送⼀一个insertObject:atIndex: 的消息参数为 foo 和5, 就是给array再下标为
5的地⽅方插⼊入⼀一个foo对象
50.OC的优缺点:
答:
优点:1、Category。可以很⽅方便的为⼀一个已有的类添加属性或者⽅方法,⽽而不需要笨拙的
去继承它。2、posing。可以让⼀一个类的对象动态的以其他类⾏行为去执⾏行,也许可以理解
成动态replace所有的method(消息转发机制,⽐比如是⽤用Person类的⼀一个对象去调⽤用⼀一个
Student类的⽅方法)3、 动态识别。 ⽐比较常⻅见的动态语⾔言的特性,涉及的点就多了,举个
简单的例⼦子,判断⼀一个对象是否是某个类的成员。isKindOfClass: 4、弹性讯息传递 。⽅方
法(method)的动态处理,譬如当你调⽤用⼀一个没有的⽅方法的时候,系统将是再运⾏行时跑出异
常⽽而不是编译时给出错误。5、 不是⼀一个过度复杂的 C 衍⽣生语⾔言 6、 Objective-C 与 C++
"16
2015年11⽉月9⽇日 星期⼀一
可混合编程
缺点: 1、不⽀支持命名空间 (写过c#等其他语⾔言的应该⽐比较清楚,可以通过命名空间将相同
名字的类进⾏行分类,⽽而objc中不得不通过前缀进⾏行区分,这也是为什么苹果的类库都有
UIXXX NSXXX等统⼀一前缀了)2、不⽀支持运算符重载3、不⽀支持多重继承4、使⽤用动态运
⾏行时类型,所有的⽅方法都是函数调⽤用,所以很多编译时优化⽅方法都⽤用不到。(如内联函数
等),性能低劣。
51.写⼀一个冒泡排序
答: int numbers[5] = {4, 14, 88, 22, 60};
int count = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (numbers[j] > numbers[j + 1]) {
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
}
}
}
52.⽤用OC写⼀一个冒泡排序
答: NSMutableArray *numbers = [@[@42, @75, @22, @14, @1, @55]
mutableCopy];
for (int i = 0; i < numbers.count - 1; i++) {
for (int j = 0; j < numbers.count - 1 - i; j++) {
if ([numbers[j] integerValue] > [numbers[j + 1] integerValue]) {
[numbers exchangeObjectAtIndex:j withObjectAtIndex:j + 1];
}
}
}
"17
2015年11⽉月9⽇日 星期⼀一
53.如何优化冒泡排序
答:添加⼀一个BOOL来标识当前的数组是否有序,在外层循环条件增加判断,⽆无序
(NO)的情况下再排序,每次进⼊入循环假设是有序的(YES),⽆无序的时候(进⼊入if条
件)把这个标识设置为⽆无序(NO),如果没有进⼊入if条件就说明当前的数组已经是有序
的,则下次循环的时候会根据添加的条件⾃自动停⽌止循环
int flag = 0;
int numbers[5] = {4, 14, 88, 22, 60};
int count = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < count - 1 && flag == 0; i++) {
flag = 1;
for (int j = 0; j < count - 1 - i; j++) {
if (numbers[j] > numbers[j + 1]) {
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
flag = 0;
}
}
}
54.写⼀一个冒泡排序的函数
void sort(int array[], int count){
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
"18
2015年11⽉月9⽇日 星期⼀一
}
}
}
}
int main(int argc, const char * argv[]) {
int flag = 0;
int numbers[5] = {4, 14, 88, 22, 60};
//如果函数实现在main函数后⾯面需要先声明sort函数
int count = sizeof(numbers) / sizeof(numbers[0]);
sort(numbers, count);
for (int i = 0; i < count; i++) {
printf("%d\n", numbers[i]);
}
return 0;
}
55.简述你对UIView、UIWindow、CALayer的理解。
答:CALayer是图层类,本⾝身可以显⽰示的,但是不能响应事件。
UIView是iOS系统中界⾯面元素的基础,所有的界⾯面元素都继承⾃自它。事件的处理由它
来执⾏行,但是显⽰示其实是由其对应的layer层来操作的,UIView内嵌了⼀一个layer,layer显
⽰示内容,UIView本⾝身增加了事件处理的功能。
UIWindow继承⾃自UIView,主要的作⽤用是作为窗⼝口呈现其他的视图。⽽而且⼀一个应⽤用
程序⼀一般情况下只有⼀一个窗⼝口。
56.在iOS中如何实现国际化?
答:iOS中国际化需要做相关的配置:
(1)选中应⽤用程序对应的project,然后添加所需要国际化的语⾔言。
(2)新建Localizable.strings⽂文件,作为多语⾔言对应的词典,存储多种语⾔言,点击右侧
Localization,勾选国际化对应的语⾔言。
(3)添加⼀一个字段,设置你想要国际化的字段
在English中,添加:SUBMIT_BTN_TITLE = Go;
"19
2015年11⽉月9⽇日 星期⼀一
在Chinese中,添加:SUBMIT_BTN_TITLE = 开始;
57.简单描述你⼀一下在开发的过程中,如何实现程序的性能优化?
答:我在开发的过程中会注意⼀一下⼏几点来优化程序性能:
1.避免庞⼤大的XIB
2.使⽤用懒加载的⽅方式延迟加载界⾯面
3.避免反复处理数据
4.避免使⽤用NSDateFormatter和NSCalendar。
5.图⽚片缓存的取舍
UIImage加载图⽚片⽅方式⼀一般有两种:
A:imagedNamed初始化
B:imageWithContentsOfFile初始化
⼆二者不同之处在于,imageNamed默认加载图⽚片成功后会内存中缓存图⽚片,这个⽅方法⽤用⼀一个
指定的名字在系统缓存中查找并返回⼀一个图⽚片对象.如果缓存中没有找到相应的图⽚片对象,
则从指定地⽅方加载图⽚片然后缓存对象,并返回这个图⽚片对象.
⽽而imageWithContentsOfFile则仅只加载图⽚片,不缓存.
⼤大量使⽤用imageNamed⽅方式会在不需要缓存的地⽅方额外增加开销CPU的时间来做这件事.
当应⽤用程序需要加载⼀一张⽐比较⼤大的图⽚片并且使⽤用⼀一次性,那么其实是没有必要去缓存这个
图⽚片的,⽤用imageWithContentsOfFile是最为经济的⽅方式,这样不会因为UIImage元素较多
情况下,CPU会被逐个分散在不必要缓存上浪费过多时间.使⽤用场景需要编程时,应该根
据实际应⽤用场景加以区分,UIImage虽⼩小,但使⽤用元素较多问题会有所凸显.
58.XMPP的优点和缺点在于什么地⽅方?
答:XMPP是⼀一种即时通讯协议,基于XML的点对点的即时通讯协议。它的优点有:
1.开放:XMPP本⾝身是开放的,所以在客户端、数据库等⽅方⾯面都有很多具体的实现和应
⽤用。
2.安全:
3.可扩展:XML命名空间的威⼒力可使任何⼈人在核⼼心协议的基础上建造定制化的功能
缺点:
只能传输⽂文本,不能传输⾳音频,视频和图⽚片。如果要传输⾳音频,视频和图⽚片,需要通过
http协议传到服务器,服务器返回⼀一个url,这个url再通过XMPP传递给对⽅方,对⽅方拿到之
后,再通过url去下载和显⽰示。
59.如何让你的应⽤用程序更加省电?
"20
2015年11⽉月9⽇日 星期⼀一
答:(1)如果程序⽤用到定位,需要在定位完毕之后关闭定位,或者降低定位的频率,不停的
定位会消耗电量。(2)如果⽤用到了蓝⽛牙,需要使⽤用蓝⽛牙时候开启蓝⽛牙,蓝⽛牙⽤用完之后关闭蓝
⽛牙,蓝⽛牙也很耗电。(3)优化算法,减少循环次数,⼤大量循环会让CPU⼀一直处于忙碌状
态,特别费电。(4)不要使⽤用⺴⽹网络轮询,使⽤用推送。(5)timer的时间间隔不宜太短,满⾜足需
求即可。(5)不要频繁刷新⻚页⾯面,能刷新1⾏行cell,不要reloadData。(6)切勿让屏幕⻓长亮。
(7)线程适量,不宜过多。
60.如何实现程序后台运⾏行?
答:苹果是不赞成程序在后台运⾏行的(因为很费电),除⾮非后台运⾏行能提升
⽤用户体验。有3种情况允许程序后台运⾏行。(1)在前台开启了⼀一个短任务,可
以在程序进⼊入后台的时候,向系统申请点时间把任务执⾏行完。(2)前台有下载
任务,可以在进⼊入后台的时候继续下载。(3)⼀一些特定类型的任务可以在后台
运⾏行,⽐比如:定位,⾳音乐播放,VoIP等等。
对于第⼀一种情况,可以调⽤用UIApplication类的
beginBackgroundTaskWithName:expirationHandler:或者
beginBackgroundTaskWithExpirationHandler:⽅方法申请⼀一点额外的时间去执
⾏行未完成的任务。调⽤用这两个⽅方法中的⼀一个都会临时延迟应⽤用程序进⼊入休眠
的时间,以便有时间把任务执⾏行完。当任务执⾏行完毕之后,应⽤用程序需要调
⽤用endBackgroundTask:⽅方法让系统知道任务执⾏行完了,程序可以进⼊入休眠状
态了。不⼀一定⾮非得等应⽤用程序要进⼊入后台了才设置后台需要执⾏行的任务。⼀一
个好的设计应该是在任务开始前就是⽤用
beginBackgroundTaskWithName:expirationHandler:或者
beginBackgroundTaskWithName:expirationHandler:添加后台执⾏行的任务,
任务⼀一旦执⾏行完就调⽤用endBackgroundTask:⽅方法告诉后台,需要进⼊入后台执
⾏行的任务执⾏行完了(此时可能还没进⼊入后台)。
对于第⼆二种情况,如果要下载⽂文件,应⽤用程序应该使⽤用NSURLSession对象
去开启下载任务,这样系统就可以在程序休眠的时候控制下载,当我们配置
NSURLSession需要在后台传输的时候,系统会单独开⼀一个进程去处理下载
任务,如果在下载的时候,进⼊入了后台,会在后台继续下载。想要实现后台
下载,我们需要配置NSURLSession。⾸首先创建⼀一个
NSURLSessionConfiguration对象,设置⼀一些属性,然后把
NSURLSessionConfiguration对象设置给NSURLSession对象。具体步骤:
(1)使⽤用backgroundSessionConfigurationWithIdentifier:创建
NSURLSessionConfiguration对象。(2)设置NSURLSessionConfiguration对
象的sessionSendsLaunchEvents 属性为YES。(3)如果在前台已经开始下载
了,还需要设置NSURLSessionConfiguration的discretionary属性为YES。
(4)为NSURLSessionConfiguration对象设置其他你需要的属性。(5)把
NSURLSessionConfiguration对象设置给NSURLSession对象。
第三种情况属于Long-Running任务。如果需要实现⻓长时任务,必须去申请,
⽽而且只有特定的类型才能⻓长时间在后台执⾏行。⽐比如:后台播放⾳音频,后台录
⾳音,后台定位和导航,VoIP,定期下载和处理新内容,定期接收外设的数
据。需要在Info.plist添加UIBackgroundModes字段选择需要后台运⾏行的事件
类型,或者在⼯工程的Capabilities⾥里⾯面打开Background Modes,勾选对应的
"21
2015年11⽉月9⽇日 星期⼀一
事件,告诉系统会在后台运⾏行什么类型的Long-Running任务,然后再根据不
同类型的事件做相应的处理即可。
"22