常用的设计模式有:代理、观察者、单例等。
(1)单例:限制一个类只会创建一个对象。对象中的属性可以存储全局共享的数据。所有类都能访问、设置此单例中的属性数据。
优点:只创建一个对象,外界访问起来方便。
缺点:一个类只有一个对象,造成责任过重,在一定程度上违背了“单一职责原则”。单例模式中无抽象层,不易于扩展。不能过多创建单例,过多创建单例会影响性能,造成系统资源浪费。
(2)观察者(KVO):它提供了观察某一属性变化的方法。当指定对象的属性发生改变后,它会自动通知相应的观察者。
优点:能提供被观察者属性的新值与旧值。用keypaths来观察属性,因此也可以观察嵌套对象。
缺点:需要手动注册观察者及移除观察者,实现observeValueForKeyPath:方法。它底层会动态创建子类(NSKVONofifying_ClassName),并实现了setter,class,dealloc,_isKVOA方法。过多有可能会对性能产生影响
(3)代理:可以实现类与类之间一对一的通信。
优点:代理协议方法能看到清晰的定义。代理方法可以设置optional或required实现方式。
缺点:需要定义协议方法,需要设置代理对象,通过代理对象实现对应的协议方法。在内存管理方面,需注意循环引用问题。
2、delegate能否实现一对多的通信?
可以,采用多播委托的方式来实现。多播委托:它是指允许创建方法的调用列表或者链表的能力。当多播委托被调用时,列表中的方法均自动执行。
普通代理只能做到一对一,无法做到一对多的回调,而多播委托则是对delegate的一种扩展与延伸,多了一个注册和取消的过程。任何需要回调的对象都必须先注册。
3、重复注册通知会有问题吗?在子线程发送通知会不会有什么影响?
不会出现问题,若多次发送同一通知,对方就会多次响应。如果重复注册通知,也会有多次响应的效果。为了避免重复注册通知而造成的错误,建议每次注册一个通知前,可以移除一次该通知。在子线程发送通知,接收通知也会在对应的子线程,此时需要注意返回到主线程执行UI刷新操作
4、iOS中实现多线程编程有哪几种方式?分别有什么区别?
(1)pthread(POSIXThreads):一套C语言编写的跨平台多线程API,使用难度大,需要手动管理线程生命周期。
(2)NSThread:面向对象操作线程,使用相对简单,需要手动管理线程生命周期。
(3)GCD:苹果多核编程解决方案,使用起来非常方便。需要自己实现如:限制并发数,任务间的依赖等功能。自动管理线程生命周期。
(4)NSOperation:基于GCD的封装,面向对象操作线程,提供了比GCD更丰富的API:限制最大并发数,设置任务依赖关系。但是它不能直接使用,因为它是一个抽象类,可以继承它或者使用系统定义NSInvocationOperation或NSBlockOperation。自动管理线程生命周期。
(2)在NSOperationQueue中,取消任务非常方便,而GCD没法停止已经加入queue的block。
(3)NSOperation能够方便的设置依赖关系,还能设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行。在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务优先级也需要大量复杂代码。NSOperationQueue还可以设置最大并发数,GCD则需要自己实现。
(4)NSOperation任务状态属性支持KVO,可以通过KVO来监听operation的就绪、取消、执行中、执行完成等状态。GCD则无法判断当前任务执行状态。
6、KVC、KVO是什么?简述KVO的实现原理。KVO是否能监听数组?如何实现?
KVC(键值编码):它是一种间接访问对象实例变量机制,可以不通过存取方法访问对象的实例变量。
KVO(键值观察):它可以使对象获取其他对象属性变化的通知机制。
KVO使用了isa-swizzling方式结合RunTime动态性,在给对象首次添加KVO时,RunTime会动态创建被监听对象的子类(NSKVONofifying_ClassName),然后实现setter,class,dealloc,_isKVOA方法,并在setter方法中实现对应通知机制。接下来将被监听对象isa指向动态创建的子类。使用KVC修改属性值时,会调用动态创建的子类中对应setter方法,触发通知机制,如此便实现了KVO。
KVO监听数组需实现NSMutableArray的增删改操作遵从KVC的规则:
并将这些接口暴露给调用者,在对数组进行操作时需使用上述实现的接口。
7、数组添加nil元素有问题吗?字典object与key可以设置为nil吗?
会有问题,数组中添加nil元素,程序会crash!会报objectcannotbenil错误。字典的key不能为nil,否则会造成崩溃。字典的object也不能为nil。通过语法糖的方式设置字典的Object,Object可以为空。如:
8、这段代码dicT[@"test"]=value中的value为nil会crash吗?不会crash。因为使用可变字典的语法糖方式会直接调用setObject:forKeyedSubscript:方法,当object为nil时,会导致可变字典中对应key的value被移除。key不能为nil,否则会直接crash,抛出NSInvalidArgumentException异常
9、在Objective-C中向nil对象发送消息会crash吗?
不会。因为OC是动态语言,调用方法时会动态转成消息发送(如:objc_msgSend(test,@selector(go)))。当你向一个对象发送消息时,runtime会根据对象的isa找到其所属类,然后在该类的缓存方法列表、方法列表、其父类方法列表中寻找对应的方法运行,在发送消息时,objc_msgSend方法无返回值(voidobjc_msgSend(idself,SELcmd,...))。那么向nil对象发送消息,第一步就直接判空返回了,因此不会出现任何问题。
10、可变数组是线程安全的吗?什么情况下不安全?怎么保证它是线程安全的?
可变数组不是线程安全的,当多个线程同时对数组进行操作时可能会导致数据错误或crash。我们可以通过加锁或者内部维护一个队列来确保它是线程安全的。
11、数组能添加一个block吗?数组添加一个block之后再取出来,这个block还有用吗?
可以,还有用,它只是多retain了一次。
12、NSMutableDictionary中的setObject:forKey:与setValue:forKey:方法有什么区别
setObject:forkey:中的value是不能够为nil的,不然会报错。
setValue:forKey:中的value能够为nil,但是当value为nil的时候,会自动调用removeObject:forKey方法。
setValue:forKey:中的key的参数只能是NSString类型,而setObject:forKey中的key是可以任意类型。
13、简述copy与mutableCopy的区别。
(1)对于非容器对象:
不可变对象:copy是浅拷贝(复制指针),mutableCopy是深拷贝(复制对象)。可变对象:copy和mutableCopy都是深拷贝(复制对象)。
(2)对于容器对象:
不可变对象:copy是浅拷贝(复制指针),深拷贝(复制对象)。可变对象:copy和mutableCopy都是深拷贝(复制对象),只是返回的对象类型不一样,前者返回的是不可变对象,后者返回的是可变对象。
容器深拷贝(复制对象)只会拷贝对象本身,对象里面的对象元素依旧是浅拷贝(复制指针)。如果要实现对象元素的深拷贝,则需要实现NSCopying/NSMutableCopying协议,并实现copyWithZone/mutableCopyWithZone方法。
14、简述weak与assign的区别。
weak用来修饰对象,不能修饰基本数据类型。assign一般用来修饰基本数据类型。ARC中如果用assign去修饰对象数据类型,则有可能造成野指针crash。weak修饰对象,在对象释放之后会把对象置为nil。
15、ARC下,不显示指定任何属性关键字时,默认的关键字有哪些?
基本数据类型--默认修饰关键字:atomic,readwrite,assign
对象数据类型--默认修饰关键字:atomic,readwrite,strong
16、weak对象会在什么时机置nil?对象在释放的时候,会调用dealloc方法,最后调用clearDeallocating函数。在clearDeallocating函数中,先根据对象地址取到所有weak指针地址的list,接着遍历list把其中数据置nil,最后从weaktable中删除对应的entry。
17、自动释放池用过吗?它是什么时候释放?什么情况下对象会被加入到自动释放池,它会加入到哪个自动释放池?多级自动释放池的释放顺序是怎样的?
在主线程中默认开启RunLoop,与此同时RunLoop会自动创建一个autoreleasepool,autorelease对象会自动被加入到autoreleasepool中,在一次RunLoop后会清空自动释放池。用__autoreleasing修饰符修饰,或类方法创建会自动加入autoreleasepool,且它会加入到最近的autoreleasepool中。多级自动释放池嵌套,释放顺序是从内到外的。
18、讲讲iOS中有哪些数据持久化方式?
iOS中数据持久化方式有以下几种:SQLite3,CoreData,文件归档,属性列表(plist文件写入)。
属性列表(plist文件):涉及的主要类是NSUserDefaults,存储小量的数据。
文件归档(archive):文件归档和解归档的对象必须实现NSCoding协议。实现initWithCoder:方法与encodeWithCoder方法。同时也建议实现NSCopying协议。
SQLite3:SQLite是一个开源的嵌入式关系数据库。可移植性好,容易使用,需要内存开销小。适合嵌入式设备。
CoreData:它底层可以使用SQLite保存数据,且无需写SQL语句。另外它还可以使用XML方式保存数据。要使用CoreData,需要在Xcode中的数据模型编辑器中设计好各个实体以及定义好他们的属性和关系。通过操作这些对象来完成数据的持久化。
19、fmdb中支持多线程吗?它是如何实现的!
fmdb是支持多线程的。它里面有个FMDatabaseQueue类。看似是一个队列,实际上不是,它继承NSObject,通过在内部创建一个Serial的dipatch_queue_t来处理inDatabase/inTransaction传入的Block,所以当我们在主线程或后台调用inDatabase/inTransaction时,代码实际上是同步的。FMDatabaseQueue这样设计是为了避免并发访问数据库时造成的线程安全问题,所有的数据库访问都是同步执行,比@synchronized与NSLock效率高。
20、简述category与extension的区别。category与extension加载的时机。
category中只能增加方法,不可添加实例变量(可通过RunTime添加属性)。extension不仅可以增加方法,还可以增加实例变量或属性(私有的)。extension不能像category一样有独立的实现部分。category是运行时决定的,extension是编译期决定的。
21、category的方法能被子类继承吗?它覆盖原有类的方法后,原有类的方法还能调用吗?如果能,你说明理由。
category方法是可以被子类继承。category并不是覆盖了类的同名方法,而是category中的方法排在了类的同名方法之前,方法的查找方式是顺序查找方法列表,因此在调用方法时,调用到的同名方法是category中的方法,从而产生了看似方法被覆盖的假象。利用运行时去遍历方法列表,可以获取到被category覆盖的方法。
22、扩展一个类的方式用继承好还是category好?请说明理由。
使用category更好。继承需要定义子类,而category不需要通过创建子类就能增加现有类的方法。用category去重写一个类的方法,仅仅只对本category有效,不会影响到其他类与原有类的关系。
23、block有几种类型?
block分为以下三种类型:
全局block:_NSConcreteGlobalBlock栈block:_NSConcreteStackBlock堆block:_NSConcreteMallocBlock
24、Swift和Objective-C有什么区别?
25、讲讲Swift的派发机制1)函数的派发机制:静态派发(直接派发)、函数表派发、消息派发2)Swift派发机制总结:
26、Swift如何显示指定派发方式?
27、Struct和Class的区别?
28、Objective-C中category为什么不能添加实例变量?
29、Objective-C中的协议默认是@optional还是@required?在使用协议的时候应当注意哪些问题?
Objective-C中的协议默是必须实现的@required,使用协议的时候应当注意循环引用问题,MRC中可以通过assign来修饰代理对象,ARC中需要使用weak来修饰代理对象,在多个协议之间采用逗号分隔。
30、category的应用场景有哪些?
31、self与super的区别?
super本质上是一个编译器标示符,它和self指向的消息接收者是同一个,它们之间不同点是:super会告诉编译器,调用class这个方法时,要去父类方法列表中找。