iOS⾯试题⼤全2021(附答案)
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);
php支持多线程吗/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之上的⼀个抽象。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论