1.INTERVIEW 共勉
2.INTERVIEW 准备
Interview preparation.png
3.iOS developers 方向
iOS developers.png
4.INTERVIEW QUESTION
4.1深copy和浅copy
浅拷贝.png
浅拷贝:
1.对内存地址的复制,让目标对象指针和源对象指向同一片内存空间.
2.内存销毁的时候,指向这片空间的指针需要重新定义才可以使用,要不然会成为野指针
3.拷贝指向原来对象的指针,使原对象的引用计数加+1
4.相当于创建了一个指向原对象的新指针,并没有创建一个新的对象.
深拷贝.png
深拷贝:
1.拷贝对象的具体内容,而内存地址是自主分配的
2.拷贝结束之后,两个对象存在的值是相同的,内存地址是不一样的
3.两个对象没有任何关系
本质区别:
1.深拷贝是内容拷贝,浅拷贝是指针拷贝
2.是否有新的内存地址
3.是否影响内存地址的引用计数.
案例一
NSString* str1 =@"copyStr";NSMutableString*str2 = [str1copy];NSMutableString*str3 = [str1 mutableCopy];NSLog(@"str1:%p--%@",str1,str1);NSLog(@"str1:%p--%@",str2,str2);NSLog(@"str1:%p--%@",str3,str3);
2018-04-1414:50:54.117652+0800MutyCopy-Copy[2644:63575]str1:0x109a48068--copyStr2018-04-1414:50:54.117885+0800MutyCopy-Copy[2644:63575]str1:0x109a48068--copyStr2018-04-1414:50:54.118010+0800MutyCopy-Copy[2644:63575]str1:0x600000259a40--copyStr
1.str1,str2地址相同,而Str3地址不同
2.NSString的copy是浅拷贝,copy返回的对象是不可变对象
3.mutablecopy是深拷贝
*案例二:
NSMutableString* str1 = [NSMutableStringstringWithString:@"mutableStr"];NSMutableString* str2 = [str1copy];NSMutableString* str3 = [str1 mutableCopy];NSLog(@"str:%p-----%@",str1,str1);NSLog(@"str:%p-----%@",str2,str2);NSLog(@"str:%p-----%@",str3,str3);
2018-04-1415:04:50.092820+0800MutyCopy-Copy[2685:70866]str:0x60000025b210-----mutableStr2018-04-1415:04:50.093059+0800MutyCopy-Copy[2685:70866]str:0x60000022ca40-----mutableStr2018-04-1415:04:50.093217+0800MutyCopy-Copy[2685:70866]str:0x60000025b540-----mutableStr
1.str1,str2,str3地址都不同
2.NSMutableString对象copy与mutableCopy都是深拷贝
3.copy返回的对象是不可变对象
4.2 iOS程序的启动过程
iOS程序的启动过程.png
首先找到程序入口,执行main函数
main -->> UIApplicationMain
创建UIApplication对象
创建UIApplication的代理对象,给UIApplication对象代理属性赋值
开启主运行循环,作用接收事件,让程序一直运行
加载info.plist,判断有没有指定main.storyboard,如果指定就去加载.
4.3 loadView
什么时候被调用?
每次访问VC的view而且view为nil,loadView方法被调用
作用
loadView方法是用来负责创建VC的view.
默认实现是怎样的?
默认实现即[spuer loadView]
1.它会先去查找与UIViewController相关联的xib文件,通过加载xib文件来创建VC的view.
2.如果在初始化VC指定了xib文件名,就会根据传入的xib文件名加载对应的xib文件.如果没有明显xib文件名,就会加载跟自己同名的xib文件.
3.如果没有找到关联的xib文件,就会创建一个空白的UIView,然后赋值给VC的view属性
4.4 单例模式
可以看我简书地址:
https://www.jianshu.com/p/9e9b405b4d94
4.5 多线程
进程
1.进程是指系统中正在运行的一个应用程序
2.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内.
线程
1.1个进程要想执行任务,必须得有线程,每1个进程至少要有1条线程
2.线程是进程的基本执行单元
3.一个进程的素有任务都在线程中执行
多线程
1.1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
2.进程--工厂,线程--工厂工人
3.多线程可以提高程序的执行效率
多线程.png
比如,我们同时开启2条线程下载文件A,文件B.
多线程的原理
1.同一时间,CPU只能处理1条线程,只有1条线程在工作
2.多线程并发执行,是CPU快速地在多条线程之间调度切换
注意:如果线程非常非常多,会发生什么情况?
cpu会在多个多线程之间进行调度,消耗大量的CPU资源.这样的话,每条线程被调度执行的频次会降低,线程执行效率降低.
多线程的优缺点
1.优点:
1.1.能适当的提高程序的执行效率
1.2.能适当调高资源利用率
2.缺点:
2.1.开启线程需要占用一定的内存空间,如果开启大量的线程,会占用大量的内存空间,降低程序的性能
2.2线程越多,CPU在调度线程上的开销就越大.
多线程应用
1.什么是主线程?
一个iOS程序运行后,默认会开启1条线程 ,称为主线程
2.主线程的主要作用?
2.1.显示/刷新UI界面
2.2.处理UI事件
3.主线程使用注意?
3.1.别将比较耗时的操作放到主线程中
3.2.耗时操作会卡住主线程,严重影响UI的流畅度
4.多线程实现技术方案?
pthread,NSThread,GCD,NSOperation四中方案.
4.6 NSThread
创建,启动线程
NSThread*thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(threadFun) object:nil];[thread start];
线程一启动,就会告诉CPU准别就绪,可以随时接受CPU调度.CPU调度当前线程之后,就会在线程thread中执行self的run方法
主线程用法
主线程相关用法.png
其他方式创建线程
创建线程后自动启动线程
[NSThreaddetachNewThreadSelector:@selector(run) toTarget:selfwithObject:nil];
隐式创建并启动线程
[selfperformSelectorInBackground:@selector(run) withObject:nil];
线程状态
线程状态.png
1.启动线程,start.就绪状态-->>运行状态.当新厂任务执行完毕,自动进入死亡状态
2.阻塞(暂停)线程,进入阻塞状态
+ (void)sleepUntilDate:(NSDate*)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
3.强制停止线程
进入死亡状态
注意:一旦线程停止了,就不能再次开启任务.
多线程的安全隐患
1.资源共享
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源.当多线程访问同一块资源时,很容易引发数据错乱和数据安全问题.
2.如图,
线程安全隐患.png
如果,多个线程访问同一块资源的话,会造成数据错乱的.
我们应该怎么解决呢?
安全隐患解决-互斥锁.png
3.如图,
线程A和线程B同时访问资源变量Integer,
为了防止抢夺资源,
线程A在读取资源变量Integer之前先加一把锁,
然后读取Integer的数据并在线程A中完成数据操作(17+1=18),
然后把数据写入Integer中,
最后开锁Unlock.在线程A对Integer操作的过程中,
线程B是无权访问Integer的,
只有线程A_Unlock后,线程B才可以访问资源变量Integer.
4.互斥锁使用格式
@synchronized(self){//需要锁定的代码}
注意: 锁定1分代码只用1把锁,用多把锁是无效的
5.互斥锁的优缺点
互斥锁的使用前提:多条线程抢夺同一块资源
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU
6.nonatomic和atomic
atomic:
原子属性,为setter方法加锁(默认就是atomic)
线程安全,需要消耗大量的资源
nonatomic:
非原子属性,不会为setter方法加锁
非线程安全,适合内存小的移动设备
4.7 GCD
什么是GCD?
全程Grand Central Dispatch,中枢调度器
纯C语言,提供了非常多强大的函数
GCD的优势
1.GCD是苹果公司为多核的并行运算提出的解决方案
2.GCD会自动利用更多的CPU内核
3.GCD自动管理线程的生命周期(创建线程,调度任务,销毁线程)
GCD有2个核心概念
1.任务:执行什么操作
2.队列;用来存放任务
任务和队列
任务和队列.png
1.执行任务
GCD中有2个用来执行任务的函数
1.1用同步的方式执行任务
dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);queue:队列block:任务
1.2用异步的方式执行任务
dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);
1.3同步和异步的区别
同步:只能在当前线程中执行任务,不具备开启新线程的能力
异步:可以再新的线程中执行任务,具备开启新线程的能力
队列的类型
GCD的队列可以分为2大类型
并发队列:可以让多个任务并发执行(并发功能只能在异步函数下才有效)
串行队列:让任务一个接着一个地执行
容易混淆的术语
有4个术语比较容易混淆:
同步,异步,并发,串行
同步,异步,并发,串行.png
注意: 同步函数 + 主队列 == 死锁
并发队列
GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_tdispatch_get_global_queue(dispatch_queue_priority_tpriority, 队列的优先级unsignedlongflags);
全局并发队列dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
全局并发队列的优先级.png
串行队列
GCD中获得串行的2中途径
1.使用dispatch_queue_create函数创建串行队列
dispatch_queue_t=dispatch_queue_create(constchar*label, 队列名称dispatch_queue_attr_tattr); 队列属性,一般用NULL即可
2.使用主队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
dispatch_queue_tqueue= dispatch_get_main_queue();
从子线程回到主线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ 执行耗时的异步操作...dispatch_async(dispatch_get_main_queue(), ^{ 回到主线程,执行UI刷新操作 });});
延时执行
设定好延迟的时间后,它会先执行后边的代码,2秒后再调用self的run方法(并且不会卡主线程,在主线程调最后会回到主线程,在子线程调最后会回到子线程)
withObject:参数afterDelay:延迟的时间[selfperformSelector:@selector(run) withObject:nilafterDelay:2.0];
使用GCD函数(2秒后自动开启新线程 执行block中的代码,不会卡主当前的线程,在主/子线程调用都可以使用)
DISPATCH_TIME_NOW:现在开始的意2.0*NSEC_PER_SEC:设置的秒数(直接更改数字即可)dispatch_get_main_queue():主队列的意思dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{2秒后执行这里的代码... 在哪个线程执行,跟队列类型有关 });
3.会卡住主线程
[NSThread sleepForTimeInterval:3]
只执行一次
使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
在设计模式中,单例模式也会用到
staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{ 程序运行过程中,永远只执行1次的代码(这里面默认是线程安全的)});
队列组
需求:1.分别异步执行2个耗时的操作,其次,等2个异步操作都执行完毕后,再回到主线程执行操作
dispatch_group_tgroup = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ 执行1个耗时的异步操作});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ 执行1个耗时的异步操作});dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 等前面的异步操作都执行完毕后,回到主线程...});
GCD的创建和释放
在iOS6.0之前,在GCD中每当使用带creat单词的函数创建对象之后,都应该对其进行一次release操作.
在iOS6.0之后,GCD被纳入到了ARC的内存管理机制中,在使用GCD的时候我们就像对待普通OC对象一样对待GCD,因此不再需要我们调用release方法.
GCD 的基本使用
GCD基本使用.png
1.异步函数+并发队列
1.创建队列(并发队列)dispatch_queue_tqueue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT); 异步函数dispatch_async(queue, ^{NSLog(@"1---%@",[NSThreadcurrentThread]); });
2.异步函数+串行队列
1.创建队列(串行队列)dispatch_queue_tqueue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL); 异步函数dispatch_async(queue, ^{NSLog(@"1---%@",[NSThreadcurrentThread]); });
3.同步函数+串行队列
1.创建队列(串行队列)dispatch_queue_tqueue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL); 同步函数dispatch_sync(queue, ^{NSLog(@"1---%@",[NSThreadcurrentThread]); });
4.同步函数+并发队列
//获得全局并发队列dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);// 同步函数dispatch_sync(queue, ^{NSLog(@"1---%@",[NSThreadcurrentThread]); });
5.异步函数+主队列
1.获得主队列dispatch_queue_tqueue = dispatch_get_main_queue(); 异步函数dispatch_async(queue, ^{NSLog(@"1---%@",[NSThreadcurrentThread]); });
6.同步函数+主队列
1.获得主队列dispatch_queue_tqueue = dispatch_get_main_queue();dispatch_sync(queue, ^{NSLog(@"1---%@",[NSThreadcurrentThread]); });
4.8 NSOperation
NSOperation作用?
配合使用NSOperation 和NSOperationQueue也能实现多线程编程
NSOperation 和NSOperationoQueue实现多线程的具体步骤
步骤.png
NSOperation的子类
NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
子类的方式有3中:
1.NSInvocationOperation
2.NSBlockOperation
3.自定义子类继承NSOperation,实现内部响应的方法
NSInvocationOperation
1.创建对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
2.调用start方法开始执行操作
- (void)start;
一旦执行操作,就会调用target的sel方法
默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作;只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSBlockOperation
1.创建NSBlockOperation对象
+ (id)blockOperationWithBlock:(void(^)(void))block;
通过addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void(^)(void))block;
只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
NSOperationQueue作用
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperationQueue中的操作
- (void)addOperation:(NSOperation*)operation;- (void)addOperationWithBlock:(void(^)(void))block;
最大并发数
同时执行的任务数
最大并发数相关的方法
-(NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
自定义NSOperation
重写-(void)main方法,在里面实现想执行的任务
重写-(void)main方法的注意点:自动创建自动释放池,如果是异步操作,无法访问主线程的自动释放池
4.9 RunLoop
如果没有RunLoop,程序输出后就退出了
intmain(intargc,char* argv[]){ NSLog(@"main");return0;}
如果有了RunLoop,由于main函数里面启动了RunLoop,所以程序并不会马上退出,保持持续运行状态
intmain(intargc,char* argv[]) {BOOLrun =YES;do{//执行各种任务,处理各种事件}while(run);return0;}
main函数中的RunLoop,UIApplicationMaiin函数内部就启动了一个RunLoop,所以UIApplicationMain函数一直没有返回,保持了程序的持续运行,这个默认启动的RunLoop跟主线程相关联
intmain(intargc,char* argv[]) {@autoreleasepool{returnUIApplicationMain(argc, argv,nil,NSStringFromClass([AppDelegateclass])); }}
RunLoop与线程
1.每条线程都有唯一的与之对应的RunLoop对象
2.主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
3.RunLoop在第一次获取时创建,在线程结束时销毁
获得RunLoop对象
获得RunLoop对象.png
RunLoop相关类
RunLoop相关类.png
Core Foundation中关于RunLoop的5个类:
CFRunLoopRef:它自己,也就代表一个RunLoop对象
CFRunLoopModeRef:RunLoop的运行模式
CFRunLoopSourceRef:事件源
CFRunLoopTimerRef:时间的触发器
CFRunLoopbaserverRef:观察者 监听CFRunLoopRef的状态
CFRunLoopModeRef
系统默认注册了5个Mode模式:
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
RunLoop处理逻辑
RunLoop处理逻辑.png
1.通知观察者,即将进入Loop
2.通知观察者,将要处理定时器
3.通知观察者,将要处理非基于端口的源
4.处理非基于端口的源
5.如果有基于端口的源准备好并处于等待状态,立即启动,跳到第9步
6.通知观察者,线程即将休眠
7.休眠,等待唤醒
8.通知观察者,线程刚被唤醒
9.处理唤醒时收到的消息,之后跳到第2步
10.通知观察者,即将推出Loop
RunLoop应用
RunLoop应用.png
RunLoop面试题
1.什么是RunLoop?
字面意思运行循环
其实它内部就是do-while循环,这个循环内部不断处理各种任务(比如Source,Timer,Observer);
一个线程对应一个RunLoop,主线程的RunLoop默认启动,子线程的RunLoop手动启动;
RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source,Timer,那么就直接退出RunLoop.
4.10 HTTP通信过程-请求/响应
HTTP通信过程.png
HTTP协议规定:1个完整的由客户端发给服务器的HTTP请求中包含以下内容
HTTP通信过程-请求.png
HTTP通信-响应.png
人才济济的iOS开发者,你凭什么脱颖而出?
与岗位要求相去甚远,如何挑战极限?
想去心怡公司,如何马到成功?
那么,你的绝招是什么呢?
在这个iOS岗位供不应求的市场,对iOS开发者对要求日益增长,小编分享一下最近对招人心得,放出47道面试题+答案,供广大iOS开发者参考
底层:
1、runloop和线程有什么关系?
2、你是否接触过OC中的反射机制?简单聊一下概念和使用
3、objc中的类方法和实例方法有什么本质区别和联系?
4、runloop的mode作用是什么?
5、1runloop是什么/runloop的概念?
6、id与instanncetype
7、什么是GDB和LLDB?
8、模拟栈操作
9、你是否接触过OC中的反射机制?简单聊一下概念和使用
10、ldb(gdb)常用的调试命令?
11、block一般用那个关键字修饰,为什么? 哪些情况会引起block循环引用,怎么解决
12、为什么很多内置的类,如TableViewController的delegate的属性是assign不是retain?
13、ViewController的didReceiveMemoryWarning是在什么时候调用的?默认的操作是什么?
14、使用block时什么情况会发生引用循环,如何解决?
15、IB中User Defined Runtime Attributes如何使用?
基础:
16、weak和unowned
17、什么是GDB和LLDB?
18、static 关键字的作用:
19、Objective-C如何对内存管理的,说说你的看法和解决方法?
20、内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
21、NSOperation queue?
22、什么是延迟加载?
23、BAD_ACCESS在什么情况下出现?
24、0x8badf00d表示是什么?
25、GCD与NSOperation这两者有什么区别?
26、单例的优弊是什么?
27、RunLoop和线程的关系: 怎么让子线程执行完成任务后不销毁
28、nonatomic和atomic对比 说说你对他们的理解。
29、两个对象之间相互通信 有哪些通信方式
30、os的数据存储有哪些方式,sqlite数据库是否支持多个线程写入数据,如果不支持怎么解决?
31、一个函数执行10次,有二次结果不正确 八次正确,你应 该怎么检查该bug。
32、1-1000 个数字,顺序打乱,又丢失了三个数字,找出丢失的三个数字。
33、Object-C有私有方法吗?私有变量呢?
34、堆和栈的区别?
35、能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
36、如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
37、dispatch_barrier_async的作用是什么?
框架:
38、第三方内存泄漏检测工具:MLeaksFinder原理分析
39、逃逸闭包
40、cocoa touch框架
网络:
41、HTTP协议详解
42、谈谈你对HTTP 、TCP、 IP、socket 协议的理解
测试:
43、单元测试那些事!
44、为什么使用这个单元测试呢?它给我们带来了什么好处呢?
45、单元测试的重要性:
swift:
46、swift闭包
47、swift中高阶函数map用法