您的位置:首页 > 移动开发 > IOS开发

那些年我面试中遇到和看过的iOS面试问题(1)2016-6-19

2020-03-01 08:19 399 查看

前言:   
   撸了几百道的关于iOS面试题,基本已经把经常会问到的问题看了两遍了,其中讲的很详细,但是面试官真的会问的你那么细吗?然而不是的,撸面试题有个前提条件,你真的懂,要不然人家在讲这个控件这个优化,你还不知道他在讲什么。
     面试官不会问你的很细,但是你要有东西讲,撸面试题不是一味的死记硬背,需要总结成自己的话语。至于怎么总结就不说了,看你的理解程度。面试官想要的结果就是你懂,你知道他这个问题的结果,你口齿清楚的表达清楚即可,一味死记硬背
  1:浪费时间脑力精力;
  2:不会总结肯定就不懂这个控件尽然都不知道肯定就不知道如何总结。
    很多人其实都懂控件,也知道这是为什么这么做,但是面试不成功就是不会总结。让你总结的原因是,你死记硬背就算背过了,面试官既然问了这常见问题,这肯定 就是常见问题。他既然面试你肯定经验丰富也会上网查询资料,知道总结的正式话语。这会给面试官一个感觉,你不懂,虽然你说上来了,但是这不是他想要的, 他想要的是听你的理解,或者说听你看完正确结果以后的总结,或者对这个控件,事件的理解。所以你要正确理解有自己方法的记忆。
      下面是我在整理的面试题,会持续更新。相当于在顺带复习第三遍吧。这些是我整理过的,不过需要你自己总结成自己的话语,这些面试题结果太过于正式。我会把 我看过的所有面试题整理出来大家一点点的来看。多看几遍这些面试题,基本上在笔试,面试问题中不是太高深的问题都不用担心了,张口即来。
   声明:下书资源均来自网络,由本人整理,由于来源杂乱,不在一一张贴来源网络链接,再次顺道鸣谢前辈大牛们的整理分享经验,不胜感激,请大牛收下我的膝盖,膜拜三叩首。
iOS面试常问到的经典题目总结 一
1、数据持久化存储方式有哪些
  四种存储方式:
1.NSUserDefaults,用于存储配置信息;
   NSUserDefaults:对象中储存了系统中用户的配置信息,开发者可以通过这个实例对象对这些已有的信息进行修改,也 可以按照自己的需求创建新的配置项。

2.SQLite,用于存储查询需求较多的数据
   SQLite擅长处理的数据类型其实与NSUserDefaults差不多,也是基础类型的小数据,只是从组织形式上不同。开发者可 以以关系型数据库的方式组织数据,使用SQL DML来管理数据。一般来说应用中的格式化的文本类数据可以存放在数据库 中,尤其是类似聊天记录、Timeline等这些具有条件查询和排序需求的数据。

3.CoreData,用于规划应用中 的对象
     CoreData是一个管理方案,它的持久化可以通过SQLite、XML或二进制文件储存。它可以把整个应用中的对象建模并进 行自动化的管理。从归档文件还原模型时CoreData并不是一次性把整个模型中的所有数据都载入内存,而是根据运行时状 态,把被调用到的对象实例载入内存。框架会自动控制这个过程,从而达到控制内存消耗,避免浪费。 无论从设计原理还是使用方法上看,CoreData都比较复杂。因此,如果仅仅是考虑缓存数据这个需求,CoreData绝对不 是一个优选方案。CoreData的使用场景在于:整个应用使用CoreData规划,把应用内的数据通过CoreData建模,完全 基于CoreData架构应用。

4.使用基本对象类型定制的个性化缓存方案.
   使用基本对象类型定制的个性化缓存方案:从需求出发分析缓存数据有哪些要求:按Key查找,快速读取,写入不影响正常 操作,不浪费内存,支持归档。这些都是基本需求,那么再进一步或许还需要固定缓存项数量,支持队列缓存,缓存过期 等。


2、沙盒的目录结构是怎样的,各自一般用于什么场合?
沙盒是某个iphone工程进行文件操作有此工程对应的指定的位置,不能逾越。
包括:四个文件夹:documents,tmp,app,Library。 手动保存的文件在documents文件里。 Nsuserdefaults保存的文件在tmp文件夹里。
  Documents 目录:您应该将所有de应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其它应该定期备 份的信息。AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以 您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
  Library 目录:这个目录下有两个子目 录:Caches 和 PreferencesPreferences 目录包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是 应该使用NSUserDefaults类来取得和设置应用程序的偏好.
   Caches 目录用于存放应用程序专用的支持文件,保存应用程 序再次启动过程中需要的信息。
  tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。
获 取这些目录路径的方法:
1,获取家目录路径的函数:
NSString *homeDir = NSHomeDirectory();
2,获取Documents目录路径的方法:
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
3,获取Caches目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDir = [paths objectAtIndex:0];
4,获取tmp目录路径的方法:
NSString *tmpDir = NSTemporaryDirectory();
5,获取应用程序程序包中资源文件路径的方法:
例如获取程序包中一个图片资源(apple.png) 路径的方法:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@”apple” ofType:@”png”]; UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath]; 代码中的mainBundle类方法用于返回一个代表应用程序包的对象。


3、如何处理多个网络请求并发的情况
通过GCD和NSOperationQueue来控制并发
https://www.geek-share.com/detail/2649075801.html
https://www.geek-share.com/detail/2548928362.html

4、NSThread中的Runloop的作用,如何使用?
每个线程(NSThread)对象中内部都有一个run loop(NSRunLoop)对象用来循环处理输入事件,处理的事件包括两类,一是来自Input sources的异步事件,一是来自Timer sources的同步事件;
run Loop在处理输入事件时会产生通知,可以通过Core Foundation向线程中添加run-loop observers来监听特定事件,以在监听的事件发生时做附加的处理工作。 主线程的Run Loop会在App运行时自动运行,子线程中需要手动运行。
Run Loop就是一个处理事件源的循环,你可以控制这个Run Loop运行多久,如果当前没有事件发生,Run Loop会让这个线程进入睡眠状态(避免再浪费CPU时间),如果有事件发生,Run Loop就处理这个事件。
如果子线程进入一个循环需要不断处理一些事件,那么设置一个Run Loop是最好的处理方式,如果需要Timer,那么Run Loop就是必须的。
开发中遇到的需要使用Run Loop的情况有:
需要使用Port或者自定义Input Source与其他线程进行通讯。
子线程中使用了定时器
使用任何performSelector*****到子线程中运行方法
使用子线程去执行周期性任务
NSURLConnection在子线程中发起异步请求

5、内存中的栈和堆的区别是什么?哪些数据在栈上哪些数据在堆上?
(1)管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生 memory leak。
 (2)申请大小:能从栈获得的空间较小,堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于计算机系统中 有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
 (3)碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块 从栈中间弹出
 (4) 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成 的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器 进行释放,无需我们手工实现。
 (5)分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈 都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。

6、runtime的概念。message send如果寻找不到相应的对象,会如何?
RunTime 简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。 对于C语言,函数的调用在编译的时候会决定调用哪个函数( C语言的函数调用请看这里 )。编译完成之后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。
http://www.jianshu.com/p/927c8384855a
如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。
如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。
(1)首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
(2)如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行
(3)如果没找到,去父类指针所指向的对象中执行1,2.
(4)以此类推,如果一直到根类还没找到,转向拦截调用。
(5)如果没有重写拦截调用的方法,程序报错。
以上的过程给我带来的启发:
重写父类的方法,并没有覆盖掉父类的方法,只是在当前类对象中找到了这个方法后就不会再去父类中找了。
如果想调用已经重写过的方法的父类的实现,只需使用super这个编译器标识,它会在运行时跳过在当前的类对象中寻找方法的过程。
拦截调用
在方法调用中说到了,如果没有找到方法就会转向拦截调用。
那么什么是拦截调用呢。
拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理。
+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后两个方法需要转发到其他的类处理
- (id)forwardingTargetForSelector:(SEL)aSelector;
(void)forwardInvocation:(NSInvocation *)anInvocation;
第一个方法是当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。
第二个方法和第一个方法相似,只不过处理的是实例方法。
第三个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。
第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。


7、#import、#include和@class有什么区别 ?#import<> 和#import“” 有什么区别?
#import是Objective-C导入头文件的关键字,
#include是C/C++导入头文件的关键字,使 用#import头文件会 自动只导入一次,不会重复导入,相当于#include和#pragma once;好处就是不会引起交叉编译。
 @class一般用于头文件中需要声明该类的某个实例变量的时候用到,它只是声明了一个类名,关于这个类的内部实现都没 有告诉编译器,在m文件中还是需要使用#import。 含;
#import<>用来包含系 统的头文件, #import””用来包含用户头文件。

8、声明并实现一个cotegory为UIView添加addNewView方法
.h
@interface UIView (AddNewView)
- (void)addNewView:(UIView *)view;
@孙振东

.m
#import "UIView+AddNewView.h"
@implementation UIView (AddNewView)
- (void)addNewView:(UIView *)view
{
    [self addSubview:view];
}
@end

9、解释HTTP中Get和Post,它们有什么区别,哪个使用时更加安全?
Http 定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个 URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。到这里, 大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。
一、原理区别
1.根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。
2.根据HTTP规范,POST表示可能修改变服务器上的资源的请求。

二、两种请求方式的区别:
1、GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。
POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。上面的item=bandsaw就是实际的传输数据。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。

2、传输数据的大小
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。
对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。

3、安全性
POST 的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据。比如,在进行登 录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的历史记录的原因,此时的用户名和密码就 很容易被他人拿到了。除此之外,GET请求提交的数据还可能会造成Cross-site request frogery攻击


10、简述在app中所应用到设计模式
(一)代理模式
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。
列表row个数delegate
自定义的delegate


(二)观察者模式
应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息。
优势:解耦合
敏捷原则:接口隔离原则,开放-封闭原则
实例:Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。
kvo,键值对改变通知的观察者,平时基本没用过。
(三)MVC模式
应用场景:是一中非常古老的设计模式,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
优势:使系统,层次清晰,职责分明,易于维护
敏捷原则:对扩展开放-对修改封闭
实例:model-即数据模型,view-视图展示,controller进行UI展现和数据交互的逻辑控制。


(四)单例模式
应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:使用简单,延时求值,易于跨模块
敏捷原则:单一职责原则
实例:[UIApplication sharedApplication]。
注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。
java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。
object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,
返回的也只是此单例类的唯一静态变量。


(五)策略模式
应用场景:定义算法族,封装起来,使他们之间可以相互替换。
优势:使算法的变化独立于使用算法的用户
敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。
实例:排序算法,NSArray的sortedArrayUsingSelector;经典的鸭子会叫,会飞案例。
注意事项:1,剥离类中易于变化的行为,通过组合的方式嵌入抽象基类
2,变化的行为抽象基类为,所有可变变化的父类
3,用户类的最终实例,通过注入行为实例的方式,设定易变行为
防止了继承行为方式,导致无关行为污染子类。完成了策略封装和可替换性。


(六)工厂模式
应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
敏捷原则:DIP依赖倒置原则
实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换
注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,
增 加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。


11、ios使用block应当注意什么?
(1)block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝。
(2) 非内联(inline) block 不能直接访问 self,只能通过将 self 当作参数传递到 block 中才能使用,并且此时的 self 只能通过 setter 或 getter 方法访问其属性,不能使用句点式方法。但内联 block 不受此限制。
(3)使用 weak–strong dance 技术来避免循环引用
(4)block 内存管理分析
block 其实也是一个 NSObject 对象,并且在大多数情况下,block 是分配在栈上面的,只有当 block 被定义为全局变量或 block 块中没有引用任何 automatic 变量时,block 才分配在全局数据段上。 __block 变量也是分配在栈上面的。在 ARC 下,编译器会自动检测为我们处理了 block 的大部分内存管理,但当将 block 当作方法参数时候,编译器不会自动检测,需要我们手动拷贝该 block 对象。
在 ARC 下,对 block 变量进行 copy 始终是安全的,无论它是在栈上,还是全局数据段,还是已经拷贝到堆上。对栈上的 block 进行 copy 是将它拷贝到堆上;对全局数据段中的 block 进行 copy 不会有任何作用;对堆上的 block 进行 copy 只是增加它的引用记数。
如果栈上的 block 中引用了__block 类型的变量,在将该 block 拷贝到堆上时也会将 __block 变量拷贝到堆上如果该 __block 变量在堆上还没有对应的拷贝的话,否则就增加堆上对应的拷贝的引用记数。
http://my.oschina.net/u/1432769/blog/390401


12.Objective-C如何对内存管理的,说说你的看法和解决方法?
  Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
   1. (Garbage Collection)自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候 开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃 圾。遗憾的是,那个高人需要消耗一定的资源,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以“Garbage Collection”不是本入门指南的范围,对“Garbage Collection”内部机制感兴趣的同学可以参考一些其他的资料,不过说老实话“Garbage Collection”不大适合适初学者研究。
  解决: 通过alloc – initial方式创建的, 创建后引用计数+1, 此后每retain一次引用计数+1, 那么在程序中做相应次数的release就好了.
   2. (Reference Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的 时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc), 然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数 器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是 Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有 Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。
  解决:一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0, 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.
  3. (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.
   解决:是由autorelease加入系统内存池, 内存池是可以嵌套的, 每个内存池都需要有一个创建释放对, 就像main函数中写的一样. 使用也很简单, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即将一个NSString对象加入到最内层的系统内存池, 当我们释放这个内存池时, 其中的对象都会被释放.

13、const意味着”只读”,关键字const什么含义? 下面的声明都是什么意思?
 const int a;

int const a;

const int *a;

int * const a;

int const * a const;

前 两个的作⽤用是⼀一样,a是⼀一个常整型数。第三个意味着a是⼀一个指向常整型数的指针 (也就是,整型数是不可修改的,但指针可以)。第四个意思a是⼀一个指向整型数的常指 针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。 最后⼀一个意 味着a是⼀一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时 指针也是不可修改的)。

欲阻⽌止⼀一个变量被改变,可以使⽤用 const 关键字。在定义该 const 变量时,通常需要对 它进⾏行初
始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本⾝身为 const,也可以指定指针所指的数据为 const, 或⼆二者同时指
定为 const;
(3)在⼀一个函数声明中,const 可以修饰形参,表明它是⼀一个输⼊入参数,在函数内部不
能改变其值;
(4)对于类的成员函数,若指定其为 const 类型,则表明其是⼀一个常函数,不能修改类 的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不 为“左值”。

14、这段代码有什么问题吗:
@implementation Person
- (void)setAge:(int)newAge {
self.age = newAge; }
@end
会死循环,会重复调用自己!self.age 改为_age即可;
并且书写不规范:setter方法中的newAge应该为age ,


 

转载于:https://my.oschina.net/rdqblogs/blog/694255

  • 点赞
  • 收藏
  • 分享
  • 文章举报
chuanzi6113 发布了0 篇原创文章 · 获赞 0 · 访问量 139 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: