美文网首页将来跳槽用
iOS 面试题 -- 2017基础篇

iOS 面试题 -- 2017基础篇

作者: 9d8c8692519b | 来源:发表于2017-09-27 16:34 被阅读84次

基础

1、为什么说Objective-C是一门动态的语言?

静态、动态是相对的,这里动态语言指的是不需要在编译时确定所有的东西,在运行时还可以动态的添加变量、方法和类。
Objective-C 可以通过Runtime 这个运行时机制,在运行时动态的添加变量、方法、类等,所以说Objective-C 是一门动态的语言。

2、为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
代理属性都用weak或是assign修饰,现整理下weak(assign)与strong(retain)修饰区别:

@property(nonatomic,weak)id<Delegate>delegate;
weak:指明该对象并不负责保持delegate这个对象,delegate这个对象的销毁由外部控制

@property(nonatomic,strong)id<Delegate>delegate;
strong:该对象强引用delegate,外界不能销毁delegate对象,会导致循环引用。

Datasource和Delegate两者的区别:
Datasource是在告诉使用者有什么属性,属性的值都是多少,是只关于数据的东西。
Delegate是在告诉使用者有什么方法可以供我调用。
即:
一个是数据,一个是操作.

block和代理的区别:
首先两者作用是一样的,都是进行单一回调。不同的是,delegate是个对象,然后用过一个对象自己调用代理协议函数来完成整个流程。block是传递一个函数指针,利用函数指针执行来进行回调。还有在内存管理上需要注意,delegate不需要保存引用。block对引用数据有copy的处理。
3、属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?
属性的组成:@property=ivar+getter+setter;

实例变量+get方法+set方法,也就是说使用@property系统会自动生成setter和getter方法;

@synthesize和@dynamic区别,在声明property属性后,有2种实现选择:

@synthesize
编译器期间,让编译器自动生成getter/setter方法。
当有自定义的存或取方法时,自定义会屏蔽自动生成该方法

@dynamic
告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告
然后由自己实现存取方法
或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由CoreData框架在程序运行的时动态生成子类属性

我们经常使用有assign,weak,strong,copy,nonatomic,atomic,readonly等关键字.
4、用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

1)因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
2)如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

为了理解这种做法,首先要知道,对非集合类对象的copy操作:
在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
[immutableObject copy] // 浅复制

[immutableObject mutableCopy] //深复制

[mutableObject copy] //深复制

[mutableObject mutableCopy] //深复制

比如以下代码:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copyNSString *stringCopy = [string copy];
查看内存,会发现 string、stringCopy 内存地址都不一样,说明此时都是做内容拷贝、深拷贝。即使你进行如下操作:
[string appendString:@"origion!"]
stringCopy的值也不会因此改变,但是如果不使用copy,stringCopy的值就会被改变。 集合类对象以此类推。 所以,
用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

参考链接:iOS 集合的深复制与浅复制

5、如何令自己所写的对象具有拷贝功能?

若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopyiog与NSMutableCopying协议。
具体步骤:
1)需声明该类遵从NSCopying协议
2)实现NSCopying协议。该协议只有一个方法:
- (id)copyWithZone: (NSZone*) zone
注意:一提到让自己的类用 copy 修饰符,我们总是想覆写copy方法,其实真正需要实现的却是“copyWithZone”方法
6、@protocol 和 category 中如何使用 @property?
1)在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
2)category 使用 @property 也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于runtime的两个函数:
①objc_setAssociatedObject
②objc_getAssociatedObject
7、为什么IBOutlet修饰的UIView也适用weak关键字?
因为当我们将控件拖到Storyboard上,相当于新创建了一个对象,而这个对象是加到视图控制器的view上,view有一个subViews属性,这个属性是一个数组,里面是这个view的所有子view,而我们加的控件就位于这个数组中,那么说明,实际上我们的控件对象是属于view的,也就是说view对加到它上面的控件是强引用。当我们使用Outlet属性的时候,我们是在viewController里面使用,而这个Outlet属性是有view来进行强引用的,我们在viewController里面仅仅是对其使用,并没有必要拥有它,所以是weak的。

如果将weak改为strong,也是没有问题的,并不会造成强引用循环。当viewController的指针指向其他对象或者为nil,这个viewController销毁,那么对控件就少了一个强引用指针。然后它的view也随之销毁,那么subViews也不存在了,那么控件就又少了一个强引用指针,如果没有其他强引用,那么这个控件也会随之销毁。

不过,既然没有必将Outlet属性设置为strong,那么用weak就好了;

一个控件可以在viewController里面有多个Outlet属性,就相当于一个对象,可以有多个指针指向它(多个引用)。

但是一个Outlet属性只能对应一个控件,也就是说,如果有button1和button2,button1在viewController里面有一个名为button的Outlet属性,此时button指向button1,但是如果用button2给button重新赋值,那么此时button指向button2。也就是说,后来的覆盖原来的。

一个控件可以在viewController里面触发多个IBAction。比如有一个button控件,在viewController里面有几个方法,那么点击button,会触发所有的这些方法。

如果我有多个控件,比如button1,button2,button3,它们也可以同时绑定一个buttonClick方法,无论点击button1,button2还是button3,都会触发这个buttonClick方法。

上面说了,button1,button2,button3有可能都触发buttonClick方法,如果想在buttonClick方法里面区分到底是哪个button触发的可能有好几种做法。

可以给这三个button各设置一个Outlet属性,然后在buttonClick里面判断sender和哪个Outlet属性是同一对象,这样就可以区分了。但是很明显,这样并不合理,因为创建的三个属性有些浪费。

我们可以给三个button各加一个tag,在buttonClick里面通过switch(或者if…)判断,sender的tag和给各个button加上的tag是否一致,如果一致则为同一对象。

要慎用tag。因为view有一个viewWithTag:方法,可以在view的子view里面找到和我们传入的tag相同的view,这样哪怕不给这个控件创建Outlet属性,也可以通过tag找到这个对象。但是很明显,这个方法要遍历子view,比较每个子view的tag,这样效率并不高,所以尽量要避免这种情况。
8、用StoryBoard开发界面有什么弊端?如何避免?

难以维护

Storyboard在某些角度上,是难以维护的。我所遇到过的实际情况是,公司一个项目的2.0版本,设计师希望替换原有字体。然而原来项目的每一个Label都是采用Storyboard来定义字体的,因此替换新字体需要在Storyboard中更改每一个Label。

幸亏我们知道Storyboard的源文件是XML,最终写了一个读取-解析-替换脚本来搞定这件事。

性能瓶颈

当项目达到一定的规模,即使是高性能的MacBookPro,在打开Storyboard是也会有3-5秒的读取时间。无论是只有几个Scene的小东西,还是几十个Scene的庞然大物,都无法避免。Scene越多的文件,打开速度越慢(从另一个方面说明了分割大故事板的重要性)。

让人沮丧的是,这个造成卡顿的项目规模并不是太难达到。

我猜想是由于每一次打开都需要进行I/O操作造成的,Apple对这一块的缓存优化没有做到位。可能是由于Storyboard占用了太多内存,难以在内存中进行缓存。Whatever,这个问题总是让人困扰的。

然而需要指出的是,采用Storyboard开发或采用纯代码开发的App,在真机的运行效率上,并没有太大的区别。

错误定位困难

Storyboard的初学者应该对此深有体会。排除BAD_EXCUSE错误不说,单单是有提示的错误,就足以让人在代码和Storyboard之间来回摸索,却无法找到解决方案。
一个典型的例子是,在代码中删除了IBOUTLET属性或者IBAction方法,但是却忘了在Storyboard中删除对应的连接,运行后crash。然而控制台只会输出一些模糊其词的错误描述。

*Terminatingappduetouncaughtexception‘NSUnknownKeyException’,
reason:‘[setValue:forUndefinedKey:]:
thisclassisnotkeyvaluecoding-compliantforthekeydrawButton.’

最后

一方面是其提供的便利,另一方面是Apple对Storyboard的大力支持。这一点宏观上看,可以在以往对Storyboard的改进和增强上看出,微观上看,几乎所有iOS8之后的simplecode都或多或少采用了Storyboard作为界面开发工具;
那改如何避免这些弊端呢,参考以下文章:

参考链接:如何避免这些弊端

9、nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?
在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备nonatomic特质,则不需要同步锁。

下面说一下atomic与nonatomic的区别:

具备atomic特质的获取方法会通过锁定机制来确保其操作的原子性。也就是说,如果两个线程同时读取一个属性,那么不论何时,总能看到有效的属性值。

如果不加锁的话(或者说使用nonatomic语义),那么当其中一个线程正在改写某属性值的时候,另外一个线程也许会突然闯入,把尚未修改好的属性值读取出来。发证这种情况时,线程读取道德属性值肯能不对。

一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:

在iOS中使用同步锁的开销比较大,这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(threadsafety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。

例如:一个线程在连续多次读取某个属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为atomic,也还是会读取到不同的属性值。

因此,iOS程序一般都会使用nonatomic属性。但是在MacOSX程序时,使用atomic属性通常都不会有性能瓶颈;

然而atomic一定是线程安全的么,回答是NO:

onatomic的内存管理语义是非原子性的,非原子性的操作本来就是线程不安全,而atomic的操作是原子性的,但并不意味着他就是线程安全的,它会增加正确的几率,能够更好的避免线程错误,但仍旧是不安全的。

为了说atomic与nonatomic的本质区别其实也就是在setter方法上的操作不同:

onatomic的实现:
-(void)setCurrentImage:(UIImage*)currentImage{if(_currentImage!=currentImage){[_currentImagerelease];_currentImage=[currentImageretain];//dosomething}}-(UIImage*)currentImage{return_currentImage;}

atomic的实现:
-(void)setCurrentImage:(UIImage*)currentImage{@synchronized(self){if(_currentImage!=currentImage){[_currentImagerelease];_currentImage=[currentImageretain];//dosomething}}}-(UIImage*)currentImage{@synchronized(self){return_currentImage;}}

Usingthe@synchronizedDirective
The@synchronizeddirectiveisaconvenientwaytocreatemutexlocksontheflyinObjective-Ccode.The@synchronizeddirectivedoeswhatanyothermutexlockwoulddo—itpreventsdifferentthreadsfromacquiringthesamelockatthesametime.Inthiscase,however,youdonothavetocreatethemutexorlockobjectdirectly.Instead,yousimplyuseanyObjective-Cobjectasalocktoken,asshowninthefollowing

example:
-(void)myMethod:(id)anObj{@synchronized(anObj){//Everythingbetweenthebracesisprotectedbythe@synchronizeddirective.}}

Theobjectpassedtothe@synchronizeddirectiveisauniqueidentifierusedtodistinguishtheprotectedblock.Ifyouexecutetheprecedingmethodintwodifferentthreads,passingadifferentobjectfortheanObjparameteroneachthread,eachwouldtakeitslockandcontinueprocessingwithoutbeingblockedbytheother.Ifyoupassthesameobjectinbothcases,however,oneofthethreadswouldacquirethelockfirstandtheotherwouldblockuntilthefirstthreadcompletedthecriticalsection.
Asaprecautionarymeasure,the@synchronizedblockimplicitlyaddsanexceptionhandlertotheprotectedcode.Thishandlerautomaticallyreleasesthemutexintheeventthatanexceptionisthrown.Thismeansthatinordertousethe@synchronizeddirective,youmustalsoenableObjective-Cexceptionhandlinginyourcode.Ifyoudonotwanttheadditionaloverheadcausedbytheimplicitexceptionhandler,youshouldconsiderusingthelockclasses.
Formoreinformationaboutthe@synchronizeddirective,seeTheObjective-CProgrammingLanguage.

当使用atomic时,虽然对属性的读和写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作,这时其他线程的读或者写操作会因为等该操作而等待。当A线程的写操作结束后,B线程进行写操作,所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行getter/setter,其他线程就得等待。如果有线程C在A线程读操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全。

更准确的说应该是读写安全,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。
其实无论是否是原子性的只是针对于getter和setter而言,比如用atomic去操作一个NSMutableArray,如果一个线程循环读数据,一个线程循环写数据,肯定会产生内存问题,这个就跟getter和setter就木有关系了。
10、实现description方法能取到什么效果?

在iOS中description方法是一个非常实用的方法,在调试程序时,经常要打印并查看对象的信息。一种办法是编写代码把对象的全部属性都输出到日志中。这时候就需要考虑实现description方法:参考链接 description的实现iOS 简单使用description方法

11、objc使用什么机制管理对象内存?
1). MRC(manual retain-release)手动内存管理;
2). ARC(automatic reference counting)自动引用计数;
3). Garbage collection (垃圾回收)但是iOS不支持垃圾回收, ARC作为LLVM(编译器的架构系统,用c++编写而成的) 3.0编译器的一项特性, 在iOS5.0 (Xcode4) 版本后推出的自动内存管理, 苹果推荐使用ARC技术来管理内存, 节约时间 , 提高效率 , 减少代码量 , 降低出错几率. 开发者不需要再手动写入retain,release,autorelease三个关键字,手动管理内存, 编译器将自动在代码合适的地方插入retain,release,autorelease进行内存管理.

ARC的判断准则, 只要没有强指针指向对象, 对象就会被释放.

内存管理机制你可以前往 iOS内功篇:内存管理

12、NSCache优于NSDictionary的几点?
NSCache 是一个容器类,类似于NSDIctionary,通过key-value 形式存储和查询值,用于临时存储对象。

注意一下它和NSDictionary区别就是:
a. NSCache 中的key不必实现copy,NSDictionary中的key必须实现copy
b. NSCache中存储的对象也不必实现NSCoding协议,因为毕竟是临时存储,类似于内存缓存,程序退出后就被释放了。
13、数据持久化的几个方案?
1.plist文件
2.NSUserDefaults
3.NSKeyedArchiver
归档在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过它实现序列化。由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议,因此,对于大多数类来说,归档相对而言还是比较容易实现的。
4.FMDB
大数据的存储我们一般用FMDB,比如聊天中的信息一般是用FMDB存储的(融云第三方就是这么存储的。。。),他是在sqlite的基础上进行封装的
5.CoreData

数据持久化可前往 iOS 常用的数据持久化

附:

面试问题只是彼此的一种沟通过程,在开发的路上还是要尽量做到深入理解。修炼好内功!
iOS 面试题 -- 2017中高级篇

相关文章

网友评论

    本文标题:iOS 面试题 -- 2017基础篇

    本文链接:https://www.haomeiwen.com/subject/ihyuextx.html