(iOS面试资料大全)
1、简述你项目中常用的设计模式。它们有什么优缺点?
常用的设计模式有:代理、观察者、单例。
(1)单例:它是用来限制一个类只能创建一个对象。这个对象中的属性可以存储全局共享的数据。所有的类都能访问、设置此单例中的属性数据。
优点:是它只会创建一个对象容易供外界访问,节约性能。
缺点:是一个类只有一个对象,可能造成责任过重,在一定程度上违背了“单一职责原则”。单例模式中没有抽象层,所以单例类的扩展有很大的困难。不能过多创建单例,因为单例从创建到程序关闭前会一直存在,过多的单例会影响性能,浪费系统资源。
(2)观察者(KVO):它提供了观察某一属性变化的方法。当指定对象的属性发生改变后,它会自动通知相应的观察者。
优点:能提供被观察者属性的新值与旧值。用keypaths来观察属性,因此也可以观察嵌套对象。
缺点:需要注册观察者,实现observeValueForKeyPath:方法,属性可以通过KVC的方法来修改。否则观察者收不到通知。
(3)代理:可以实现类与类之间一对一的通信。
优点:代理协议方法都有清晰的定义。代理方法可以设置可选或必须实现。
缺点:需要定义协议方法,需要设置代理对象,代理对象实现协议方法。内存管理方面,需要注意循环引用问题。
2、代理模式能否实现一对多的通信?
可以,采用多播委托的方式来实现。多播委托:它是指允许创建方法的调用列表或者链表的能力。当多播委托被调用时,列表中的方法均自动执行。
普通代理只能是一对一的回调,无法做到一对多的回调,而多播委托正式对delegate的一种扩展与延伸,多了一个注册和取消的过程。任何需要回调的对象都必须先注册。
3、 重复注册通知会有问题吗?
不会出现问题,若多次发送同一通知,对方就会多次响应。如果重复注册通知,也会有多次响应的效果。为了避免重复注册通知而造成的错误,建议每次注册一个通知前,可以移除一次该通知。
4、项目中是否用过多线程编程?简述你常用的多线程实现方式?
常用的是GCD。
GCD是苹果开发中一个多核编程的解决方案。GCD的队列分为两种类型,SerialDispatchQueue与ConcurrentDispatchQueue。系统默认提供了一个dispatch_get_main_queue,一个dispatch_get_global_queue。
5、简述NSOperationQueue与GCD的区别。
(1) GCD是底层的C语言构成的API。NSOperationQueue及相关对象是Objc对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构。而Operation作为一个对象,为我们提供了更多的选择。
(2) 在NSOperationQueue中,我们可以取消任务,而GCD没法停止已经加入queue的block。
(3) NSOperation能够方便的设置依赖关系。还能设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行。在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务优先级也需要大量复杂代码。
NSOperation还可以设置并发数。
6、实现多线程有哪几种方式?
(1)NSThread:detachNewThreadSelector:
(2)继承NSOperation
(3)GCD:dispatch_async
(4)NSObject:performSelectorInBackground:
7、KVC、KVO是什么?简述KVO的实现原理。KVO能否监听数组?如何实现?
KVC:键值编码,它是一种间接访问对象实例变量机制,可以不通过存取方法访问对象的实例变量。
KVO:键值观察,它可以使对象获取其他对象属性变化的通知机制。
KVO是根据isa-swizzling技术来实现的,主要依据runtime的强大动态能力。当给某个对象第一次添加KVO监听的时候,运行时会动态的创建一个被监听对象的派生类,然后重写KVO需要监听属性值对应的setter方法,在这个setter方法中实现了通知机制。最后将被监听对象的isa指向动态创建的派生类。这样当使用KVC修改属性值时,就会调用动态创建的派生类中对应的setter方法,触发通知机制,从而实现了KVO了。
KVO可以监听数组。
实现 NSMutableArray 的增删改操作遵从 KVC 的规则,需要实现其对应方法:
增操作 -insertObject:inAtIndex: 或者 -insert:atIndexes:
删操作 -removeObjectFromAtIndex: 或者 -removeAtIndexes:
改操作 -replaceObjectInAtIndex:withObject: 或者 -replaceAtIndexes:with:
并将这些接口暴露给调用者,在对数组进行操作时需使用上述实现的接口。
8、简单说下C++,JAVA,Objective-C这几种语言有什么区别?
Objective-C与JAVA都是单继承语言,C++是多继承语言。
Objective-C不支持命名空间机制,通过类名前面加前缀NS来区分。
Objective-C与JAVA不支持运算符重载。
Objective-C协议可选实现,JAVA的接口必须实现。
9、数组添加nil元素有问题吗?字典object与key可以设置为nil吗?
会有问题,数组中添加nil元素,程序会崩溃!报object cannot be nil错误。字典的key不能为nil,否则会造成崩溃。字典的object也不能为nil。
10、oc中向一个nil对象发送消息会出现问题吗?
不会出现问题,因为objc是动态语言,每个方法在运行时会被动态转为消息发送。即:objc_msgSend(receiver,selector)。objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。 那么,回到本题,如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。
11、可变数组是线程安全的吗?什么情况下不安全?可以加锁吗?它锁住的是添加元素操作还是数组对象?
可变数组不是线程安全的,在异步读取数据的情况下是不安全的。可以加锁,锁住的是数组。
12、数组能添加一个block吗?数组添加一个block之后再取出来,这个block还有用吗?
可以,还有用,它只是多retain了一次
13、NSMutableDictionary中的setObject:forKey:与setValue:forKey:方法有什么区别?
setObject:forkey:中value是不能够为nil的,不然会报错。
setValue:forKey:中的value能够为nil,但是当value为nil的时候,会自动调用removeObject:forKey方法。
setValue:forKey:中的key的参数只能是NSString类型,而setObject:forKey是可以任何类型。
14、简述copy与mutablecopy的区别。
(1)非容器对象:
对不可变对象:copy是指针复制(浅拷贝), mutableCopy是对象复制(深拷贝)。
对可变对象:copy和mutableCopy都是对象复制。
(2)容器对象:
对不可变对象:copy是指针复制,mutableCopy是对象复制。
对可变对象:copy和mutableCopy都是对象复制,只是返回的对象类型不一样,前者返回的是不可变对象,后者返回的是可变对象。
容器对象复制仅限于对象本身,对象元素仍然是指针复制。
15、简述weak与assign的区别。
weak用来修饰对象,不能修饰基本数据类型。assign一般用来修饰基本数据类型。weak修饰对象,在对象释放之后会把对象置为nil。
16、ARC下,不显示指定任何属性关键字时,默认的关键字有哪些?
基本数据类型默认修饰关键字:atomic,readwrite,assign
普通的OC对象默认修饰关键字:atomic,readwrite,strong
17、weak在什么时候置空?
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
我们可以设计一个函数(伪代码)来表示上述机制:
objc_storeWeak(&a, b)函数:
objc_storeWeak函数把第二个参数–赋值对象(b)的内存地址作为键值key,将第一个参数–weak修饰的属性变量(a)的内存地址(&a)作为value,注册到 weak 表中。如果第二个参数(b)为0(nil),那么把变量(a)的内存地址(&a)从weak表中删除,
你可以把objc_storeWeak(&a, b)理解为:objc_storeWeak(value, key),并且当key变nil,将value置nil。
在b非nil时,a和b指向同一个内存地址,在b变nil时,a变nil。此时向a发送消息不会崩溃:在Objective-C中向nil发送消息是安全的。
而如果a是由assign修饰的,则: 在b非nil时,a和b指向同一个内存地址,在b变nil时,a还是指向该内存地址,变野指针。此时向a发送消息极易崩溃。
下面我们将基于objc_storeWeak(&a, b)函数,使用伪代码模拟“runtime如何实现weak属性”:
// 使用伪代码模拟:runtime如何实现weak属性
id obj1;
objc_initWeak(&obj1, obj);
/obj引用计数变为0,变量作用域结束/
objc_destroyWeak(&obj1);
下面对用到的两个方法objc_initWeak和objc_destroyWeak做下解释:
总体说来,作用是: 通过objc_initWeak函数初始化“附有weak修饰符的变量(obj1)”,在变量作用域结束时通过objc_destoryWeak函数释放该变量(obj1)。
下面分别介绍下方法的内部实现:
objc_initWeak函数的实现是这样的:在将“附有weak修饰符的变量(obj1)”初始化为0(nil)后,会将“赋值对象”(obj)作为参数,调用objc_storeWeak函数。
obj1 = 0;
obj_storeWeak(&obj1, obj);
也就是说:
weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)
然后obj_destroyWeak函数将0(nil)作为参数,调用objc_storeWeak函数。
objc_storeWeak(&obj1, 0);
前面的源代码与下列源代码相同。
// 使用伪代码模拟:runtime如何实现weak属性
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
/* … obj的引用计数变为0,被置nil … */
objc_storeWeak(&obj1, 0);
objc_storeWeak函数把第二个参数–赋值对象(obj)的内存地址作为键值,将第一个参数–weak修饰的属性变量(obj1)的内存地址注册到 weak 表中。如果第二个参数(obj)为0(nil),那么把变量(obj1)的地址从weak表中删除,在后面的相关一题会详解。
以上内容总结如下:
(1)从weak表中获取废弃对象的地址为键值的记录
(2)将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
(3)从weak表中删除该记录
(4)从引用计数表中删除废弃对象的地址为键值的记录。
18、自动释放池用过吗?它是什么时候释放?什么情况下对象会被加入到自动释放池,它会加入到哪个自动释放池?
主线程默认开启runloop,同时runloop会自动创建一个autoreleasepool,autorelease对象会自动被加入autoreleasepool中,一次runloop后清空自动释放池。用__autoreleasing修饰符修饰,或类方法创建会自动加入autoreleasepool。它会加入到最近的autoreleasepool中。
19、你知道iOS中有哪些数据持久化方式吗?请简要加以说明。
iOS中数据持久化方式有:SQLite3数据库,CoreData,文件归档,属性列表(plist文件写入)。
属性列表:涉及的主要类是NSUserDefaults,存储小量的数据。
文件归档:对象必须实现NSCoding协议。实现initWithCoder:方法与encodeWithCoder方法。同时也建议实现NSCopying协议。
SQLite3数据库:SQLite是一个开源的嵌入式关系数据库。可移植性好,容易使用,需要内存开销小。适合嵌入式设备。
CoreData:它可以使用SQLite保存数据,而且不需要写SQL语句。另外它还可以使用XML方式保存数据。要使用CoreData,需要在Xcode中的数据模型编辑器中设计好各个实体以及定义好他们的属性和关系。通过操作这些对象来完成数据的持久化。
20、fmdb中支持多线程吗?它是如何实现的!
支持多线程。它里面有个FMDatabaseQueue类。它看似是一个队列,实际上它本身并不是,它继承NSObject,通过内部创建一个Serial的dipatch_queue_t来处理inDatabase和inTransaction传入的Block,所以当我们在主线程(或者后台)调用inDatabase或者inTransaction时,代码实际上是同步的。FMDatabaseQueue如此设计的目的是为了避免并发访问数据库的线程安全问题,所有的数据库访问都是同步执行,比@synchronized与NSLock效率高。
21、简述category与extension的区别。Category与extension加载的时机。
category中只能增加方法,不可添加实例变量(可添加属性)。extension不仅可以增加方法,还可以增加实例变量或属性(私有的)。extension不能像category一样有独立的实现部分。category是运行时决定的。extension是编译期决定的。
22、category的方法能被子类继承吗?它覆盖原有类的方法后,原有类的方法还能调用吗?如果能,你说明理由。
category的方法可以被子类继承。category并不是绝对的覆盖了类的同名方法,而是category的方法排在了类的同名方法之前,方法的检索方式是顺序检索,所以在调用方法时,调用到的同名方法是category,从而产生了覆盖。
利用运行时遍历方法列表,可以调用被category覆盖的方法。
23、 扩展一个类的方式用继承好还是category好?请说明理由。
用类目好。因为继承还需要定义子类。类目不需要通过创建子类来增加现有类的方法。用category去重写一个类的方法,仅仅只对本category有效,不会影响到其他类与原有类的关系。
24、 block有几种类型?block的实现?
block分为三种类型:
_NSConcreteGlobalBlock
_NSConcreteStackBlock
_NSConcreteMallocBlock
block:匿名函数
25、 Swift用的多吗?简单的说说1.0与2.0的区别。
swift2.0新增:guard语句,异常处理,协议扩展,打印语句改变,avaliable检查,do-while语句重命名:repeat-while,defer关键字
26、 在Swift用有没有用过defer关键字?
对defer语句进行的延迟,函数结束时调用。
27、 SDWebImage的图片保存在什么位置?
图片保存在沙盒中的library/caches文件夹下。
28、 Objective-C中类目为什么不能添加实例变量?
因为在运行时,对象的内存布局已经确定,如果添加实例变量会破坏类的内部布局。
29、 Objective-C中的协议默认是@optional还是@require?在使用协议的时候应当注意哪些问题?
Objective-C中的协议默是必须实现的@require,使用协议的时候应当注意循环引用问题,多个协议之间采用逗号分隔。
30、 Objective-C的协议与JAVA中的接口有什么区别?
OC中的协议可选实现,JAVA中的接口必须实现。
31、 类目的应用场景有哪些?
(1)可以把类的实现分开在几个不同的文件里面
(2)声明私有方法
(3)模拟多继承
(4)把Framework的私有方法公开
32、 self与super的区别?
super本质上是一个编译器标示符,它和self指向的是同一个消息接受者,两者不同在于:super会告诉编译器,调用class这个方法时,要去父类方法,而不是本类方法。
33、 图片缓存为什么不保存到沙盒下的tmp文件目录中?
因为tmp文件夹是用来存放临时文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除。
34、 NSURLConnection与NSURLSession。
NSURLConnection它是CoreFoundation/CFNetwork框架的API之上的一个抽象。
NSURLConnection这个名字实际指代Foundation框架的URL加载系统中一系列有关联的组件:NSURLRequest、NSURLResponse、NSURLProtocol、NSURLCache、NSHTTPCookieStorage、NSURLCredentialStorage以及同名类NSURLConnection。NSURLRequest 被传递给 NSURLConnection。被委托对象(遵守以前的非正式协议 和 )异步地返回一个 NSURLResponse 以及包含服务器返回信息的 NSData。
NSURLSession包括:NSURLRequest、NSURLCache、NSURLSession、NSURLSessionConfiguration、NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask。
它与NSURLConnection的区别在于:NSURLSession最直接的改进就是可以配置每个Session的缓存,协议,cookie以及正式策略。甚至跨程序共享这些信息。每个NSURLSession对象都由一个NSURLSessionConfiguration对象来进行初始化。
session task:负责处理数据的加载以及文件和数据在客户端与服务器之间的上次和下载。
35、 简述ARC与MRC的区别。
ARC:自动引用计数。MRC:手动引用计数。ARC是把内存交给系统管理。系统会在编译的时候自动插入retain/release。MRC则需要手动管理对象的引用计数。当你alloc,new,copy,mutablecopy或者retain一个对象时,你就有义务向它发送一条release或autorelease消息。