基础面试题:
1. copy 和 strong 的区别。
也就是 深拷贝和浅拷贝 的区别。 copy属于深拷贝 其性质是开辟新的内存空间指向新的值,不同的内存地址互不干涉,使⽤ copy 的⽬的是为了让本对象的属性不受外界影响。strong属于浅拷贝 那么这个属性就有可能指向⼀个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性
常会被问到!!!
1. NSMutableArray为什么不可以用 copy 修饰?
其原因是:在调用self.数组 增删改 系统方法的时候会奔溃。 copy 修饰的 NSMutableArray的数组会被当成 NSArray 不可变数组执行, 当self 调用底层的 get/set 方法时候 没有响应的增删改方法。所以会奔溃。
经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作!
2. NSString 为什么用copy 修饰?
使用copy修饰之后,即使属性拷贝来自可变字符串,也会被深拷贝成不可变字符串, 例如在字符串appendString 拼接或者是等等操作时。开辟的是另一块新的内存空间、修改前的值不会随着修改后的值而发生变化。所以为了避免可变字符串类型的值被修改用copy
weak 和 assign 的区别:
weak: 只可以修饰对象、不会产生野指针,释放后指针会被置为 nil , 之后再向该对象发送消息也不会奔溃
assign: 既可以修饰对象也可以修饰基本数据类型。 修饰对象会产生野指针、释放后不会置为 nil ,发送消息会奔溃
2. 关于常见的block问题
1. block 为什么用copy修饰?
2. _ _ block 为什么可以修改block变量
3. 关于block 循环引用的问题
来自内心灵魂深处的三问!!!
block 为什么用copy修饰?
首先得理解 堆和栈 的关系! 堆是程序员 手动的分配和释放内存、栈是由 系统分配释放内存。
说到在类中声明一个block为什么要用copy修饰的话,那就要先说block的三种类型
1> NSGlobalBlock 全局block 不会访问外部的变量! 就说你的block没有调用其他外部的变量,那么你的block就是这种,例如: 你的block 只写一个了个NSLog的输出
2> NSStaticBlock 保存在栈上的block,当函数返回时被销毁。这个block申请不用copy修饰,并且你的block访问了外部变量
3> NSMallocBlock 保存在堆中的block,当引用计数为0时会销毁,也就是用copy修饰的blcok
我们知道,函数的声明周期是随着函数调用的结束就终止了。我们的block是写在函数中的。
如果是全局静态block的话,他直到程序结束的时候,才会被被释放。但是我们实际操作中基本上不会使用到不访问外部变量的block。
如果是保存在栈中的block,他会随着函数调用结束被销毁。从而导致我们在执行一个包含block的函数之后,就无法再访问这个block。因为(函数结束,函数栈就销毁了,存在函数里面的block也就没有了),我们再使用block时,就会产生空指针异常。
如果是堆中的block,也就是copy修饰的block。他的生命周期就是随着对象的销毁而结束的。只要对象不销毁,我们就可以调用的到在堆中的block。
这就是为什么我们要用copy来修饰block。因为不用copy修饰的访问外部变量的block,只在他所在的函数被调用的那一瞬间可以使用。之后就消失了。
_ _ block 为什么可以修改block变量 ?
__block修饰之后是将变量的内存地址传进去,Block捕获时,记录了该变量的地址。所以后续该变量的值改变了,block调用时,通过地址获取到的值仍然是最新的值
关于block 循环引用的问题 ?
一个对象中强引用了block,在block中又强引用了该对象,就会发射循环引用。
block中使用self,self.xxBlcok或者成员变量block 导致了循环引用 。 __weakSelf来代替self解决等。 __weak __tyof (self) weakselfi = self
这里常常会有一个延伸: 问的是AFNetWorking 中的Block使用怎么会不引起循环引用?
AF3.0 一下 AFURLConnectionOperation 里的一个请求结束之后,setCompleteBlock会把block设置为nil,来打破循环引用 .
所谓循环引用,是因为当前控制器在引用着block,而block又引用着self即当前控制器,这样就造成了循环引用。AFN中的block的调用并不在当前控制器中调用,那么这个self就不代表当前控制器,那自然也就没有循环引用的问题
3. ios13适配问题
1. 私有方法 KVC 可能导致崩溃。在 iOS 13 中部分方法属性不允许使用 valueForKey、setValue:forKey: 来获取或者设置私有属性,具体表现为在运行时会直接崩溃
2. 使用 presentViewController 方式打开视图,默认的如下图所示的视差效果,通过下滑返回。解决方法就是将 modalPresentationStyle 改回 Fullscreen 样式
3.MPMoviePlayerController 被弃用
4.在 iOS 13 中,苹果将原来蓝牙申请权限用的 NSBluetoothPeripheralUsageDescription 字段,替换为 NSBluetoothAlwaysUsageDescription 字段。
5. 第三方登录中心必须接入苹果登录
等等有一些导航栏 搜索框的改变 这上面我就不一一列举了。有心得同学可以自己去查阅一下文档
4. Runloop的简单理解
第一次获取时被创建、线程结束被销毁。 多线程时 主线程默认开启 子线程的手动开启
[NSRunLoop currentRunLoop]
概念: 运行循环机制。内部是一个do-while 循环,在这个循环内部不断处理各种任务, RunLoop存在的目的就是当线程中有任务的时候,保证线程干活,当线程没有任务的时候,让线程睡眠,提高程序性能,节省资源,该做事的时候做事,该休息的时候休息
作用: 1> 保持程序持续运行
2> 处理App中各类事件 (事件响应、手势识别、界面刷新、自动释放池、等事件处理)
3> 节省资源,提高程序性能
这边有一个延伸问题: UIScrollView 的滚动会导致 NSTimer 失效为什么?
在tableview滑动时timer就是显示暂停,原因是timer的这个简便构造方法把timer加入了NSRunLoopDefaultMode上,而tableview在滑动时只会处理UITrackingRunLoopMode,也就是说当前的RunLoop并没有功夫处理timer事件。 指定消息循环的模式为CommonModes(无论runloop运行在哪个mode,都能运行)
- kCFRunLoopDefaultMode, App的默认运行模式,通常主线程是在这个运行模式下运行
- UITrackingRunLoopMode, 跟踪用户交互事件(用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响)-
kCFRunLoopCommonModes, 伪模式,不是一种真正的运行模式- UIInitializationRunLoopMode:在刚启动App时第进入的第一个Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode:接受系统内部事件,通常用不到
注意iOS 对以上5中model进行了封装NSDefaultRunLoopMode;NSRunLoopCommonModes
为什么说提高了性能?
因为在没有事件发生的时候处于休眠状态,有事件发生的时候处于工作状态 。
5. SDWebimage 原理相关问题!!!
我这里只做一个简述。 只针对面试可能遇到的一些提问。具体问题你可以去查询一下源码或者找一些大神的博客看一看。
其作用: 就是图片的下载、缓冲、下载进度监控。
1. 缓冲图片的名称 MD5 为防止重名
2.内存警告是如何处理的 ? 利用通知中心观察 、clear Memory清理内存缓冲 clear Disk 清理磁盘缓冲。
3.缓冲时间是一周
4.clear和clean的区别?
clear 连同缓冲文件夹删除、再创建一个新的。clean 先删除过期的文件、再计算缓冲大小删除、按照创建时间删除知道maxsize
5.默认缓冲文件路径 : default下 6. 最大并发数6条 7.超时时间15S 8.缓冲机制:NSCache
SDWebimage的工作流程:
1. setImageWithURL 先设置请求网址和占位图片
2. 先从内存图片查找图片, 如果查到回调到 SDWebImaFFgeManage中显示图片
3. 如果有没查到就 生成opeartion 添加到队列查找磁盘、根据 URLKey从缓冲目录读取。读取到添加到内存缓冲中、再回调到manage类中展示
4. 如果没换没有查询到 就重新下载
5. 下载完的图片加入缓存中,并写入到磁盘中;
6.整个获取图片的过程是在子线程中进行,在主线程中显示。
6. weak如何实现自动赋nil?
runtime 对注册的类会进行布局,weak对象会被放到一个弱引用的hash表中。用weak指向的对象内存地址作为key。当此对象的引用计数为0的时候dealloc,在这个weak表中搜索找到所有以key为键weak的对象从而设置为nil
7.自动释放池问题
常用到是for循环创建大量变量的时候 !!!
每次调用每次都会创建一个新的对象。如果不是通过alloc、new、copy等创建的,那么他们的内部都会有一个autorelease进行自动释放,会等到循环结束进行释放而此时会消耗大量内存资源造成内存溢出。运行缓慢 。autorelease实际上只是把对release的调⽤延迟了,对于每⼀个autorelease,系统只是把该Object放⼊了当前的Autorelease pool中,runloop结束或者作用域超出{}或者超出[pool release]之后再被释放
@autoreleasepool 可以完美的解决
8. 涉及到地图持续定位耗电问题解决方案
原因是:持续的调用didupdatelocation方法
解决方案“:
1. 距离过滤器 distanceFilter 指定距离调用代理方法
2. 设置精确度 通过计算过程的降低达到省电
9. set/get 方法返回self属性奔溃
self. 实际上相当于 set/get 的调用。 导致循环调用 所以会奔溃
11. KVO原理
因为时间问题我就不贴代码了。只是将KVO建立观察者后 的一个流程梳理一下。
1、利用RuntimeAPI动态生成一个子类NSKVONotifying_XXX,并且让instance对象的isa指向这个全新的子类
2、当修改对象的属性时,会在子类NSKVONotifying_XXX调用Foundation的_NSSetXXXValueAndNotify函数
3、在_NSSetXXXValueAndNotify函数中依次调用
1) willChangeValueForKey 2)父类原来的setter 3)didChangeValueForKey,didChangeValueForKey:内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
2、如何手动触发KVO方法
手动调用willChangeValueForKey和didChangeValueForKey方法
3、直接修改成员变量会触发KVO吗
不会触发KVO,因为KVO的本质就是监听对象有没有调用被监听属性对应的setter方法,直接修改成员变量,是在内存中修改的,不走set方法
4、不移除KVO监听,会发生什么
不移除会造成内存泄漏但是多次重复移除会崩溃。系统为了实现KVO,为NSObject添加了一个名为NSKeyValueObserverRegistration的Category,KVO的add和remove的实现都在里面。在移除的时候,系统会判断当前KVO的key是否已经被移除,如果已经被移除,则主动抛出一个NSException的异常
5. 如何手动关闭kvo
重写被观察对象的automaticallyNotifiesObserversForKey方法,返回NO重写automaticallyNotifiesObserversOf ,返回NO å 注意:关闭kvo后,需要手动在赋值前后添加willChangeValueForKey和didChangeValueForKey,才可以收到观察通知。
11. delegate 代理用weak 修饰?
weak修饰对象只指向对象,并不保持delegate对象,释放由外部控制
strong该对象强引用delegate对象,形成稳妥对象和被委托对象相互拥有,由于外部不能销毁而导致引起循环引用问题。
12. 关于图片的上传和压缩问题
AF中的 AFHTTPSessionManager就可以实现。主要涉及的就是如果是多张图上传50张、500张考虑一个占用内存过大而崩溃的问题,可以通过加到自动释放池解决。
压缩问题: 例如传的图片大、耗时 耗流量
可以通过 UIImageJPERepresontation方法。可以设置压缩指数。 例如加入判断 image>1M 压缩指数设置为0.7 0.5M<imahe<1M 调整成0.8 小于0.5M 写成0.9或者1 都可以
13.TableView优化
1.提前计算好cell的高度和布局(因为tableview回调的时候会多次的调用heightForRow的方法),获取数据的时候计算cell,保存到对应的model中,调用行高直接获取model 就行
2..加载网络数据,下载图片,使用异步加载,并缓存
3.滑动时按需加载对应的内容,
4.使用局部更新(如果只是更新某组的话,使用reloadSection进行局部更)
14.性能优化
1. 页面布局: 对象的创建、销毁(广播、定时器等等),文本控件的计算排版等等
1.2 尽量使用纯代码开发。 避免庞大的xib,storyBoard 因为加载到转化到代码需要时间
2.卡顿优化: 2.1 尽量提前计算好布局,一次性调整对应属性避免多次修改
2.2 使用多线程。如果线程开辟太多的话可以结合信号量(消耗CPU)
2.3 数据量大的时候考虑使用本地数据(尽量一次性写入,避免频繁写入数据)
3. 网络优化: 1. E网、4G、WiFi下设置不同的超时时间在用户体验上显的比较体验好
2. 使用后台会话
3. 使用缓冲减少网络请求
4. 耗电优化: 一般分为几个分为几个方面吧 比如网络、GPS定位服务、涉及到硬件方面(蓝牙了 相机了)等等。
1.首先是网络方面 涉及到视频、直播之类 我们知道蜂窝对电量的消耗远远大于Wife信号、因为它是使用多个并发信号、并且会定期的扫描寻找附近更强的信号、所以我可以做一个对于网络状态的监听 如过没wife 提示用户是否要播放视频、直播等等、包括分辨率、音频考虑是否可以相对降低。 尽量减少同一操作 使用缓存 定位服务、蓝牙方面 降低他们的定位或者是发送/搜索广播的频率、当用户进入后台 离开特定的页面 关闭位置的追踪和蓝牙功能等等
2.涉及到蓝牙 、广播等等 设置一个合理的请求时间
6.内存优化: 1. 涉及到通知、定时器等等释放的问题
2. tableview. 行高了提前计算避免多次调用行高方法,数据大分页显示
3. 图片处理,尽量和UIImageView大小相同避免运行中缩放,多做缓冲
4. 大量的临时对象加入Autorelease Pool中执行,避免内存过高
5. 数据库的储存问题
15. 信号量
应用场景: 访问有限的资源、 或者是多线程中的一些特定请求
主要用到的函数: dispatch_semaphore_create创建信号量设置初始值
dispatch_semaphore_signal 发送信号,信号量+1
dispatch_semaphore_wait 等待信号 , 如果大于零则减掉1个信号量,往下执 行,如果等于零则阻塞该线程
1. 多个网络请求无序返回后再刷新界面?
创建线程组dispatch_group_create,最后再notify中刷新界面。结合信号量使用这样可 以保证在没有所有数据返回之前,notify里的内容一直不会执行
2. 多个网络请求有序返回输出?
1.信号量
2. 用NSOPerationQueue中的依赖关系 [ A addDependency:B ]
16. 关于折线图、柱形、饼状图
Chares 图标库(swift写的),需要创建一个桥接,oc-swift
或者是在UIView函数方法drawRect中自定义贝塞尔曲线 画柱形设置(x,y,w,h) 设置填充颜色等等
17. AFNetWorking 实现原理:
主要是用于对网络数据的请求和实时监控网络的状态,由五个部分组成:
1. AFURLSessionManage:核心类,负责请求的建立、管理和销毁等功能
2. AFURLRequest: 请求头的解码、序列化、优化处理、简化拼接过程
3. AFURLRepose: 用于网络返回数据的处理
4. AFNetWork : 监控网络请求的变化
5. UIKit:对于iOS UIKit的扩展库
这里会常常被问到: AFN网络请求的block内使用self不会造成循环引用?
答案是:AFNetworking是封装了一个completionBlock,AFURLConnectionOperation 里的一个请求结束之后,setCompleteBlock会把block设置为nil,来打破循环引用 .
18. 沙河目录分析:
分为三个目录: Documents、 Library( Caches、Preferences )、 tmp
Documents:保存应用运行需要持久化的数据, sqlite数据库
tmp: 保存运行时所需的临时数据
Library-->Preferences : NSUserDefaults
19 atomic和nonatomic的区别
1. atomic是原子性的(原子性则表示这是一个不可再分的操作),nonatomic是非原子性的
2. nonatomic和atomic系统自动生成的setter和getter方法不一样,atomic生成的是有加锁的,而nonatomic是没有加锁的(因此,nonatomic的线程不安全,但访问速度快,而atomic由于加速,访问速度会比nonatomic的要慢,但不一定是线程安全的)
3.atomic加锁只是保证setter和getter的有序性
比如:线程1调用了A的setter方法,当还没调用完时,线程2调用A的getter方法,但这时候线程2的调用要等线程1的完成才能进行,但是线程1调用了getter,线程2调用了setter,线程3调用了setter。那么这是后线程1调用getter后得出的值是不确定的,有可能是线程2操作后的值,也可能是线程3操作后的值
20. HTTPS 协议
1.向后台开发者获取SSL证书(crt格式),并将该文件的格式转换成cer格式
2. 双击该文件,在 keychain (钥匙串访问)中找到该文件的证书,项目然后导出cer文件。
3. AFN 3.0配置 先导入证书 证书由服务端生成,使用证书验证模式。 如果是需要验证自建证书,
4. info.plist,配置白名单
22. runtime面试题
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数\平时编写的OC代码,在程序运行过程中,其实最终会转换成Runtime的C语言代码,将尽可能多的决策从编译时和链接时推迟到运行时
基本作用: 动态的创建类、动态添加修改这个类的属性和方法、消息传递转发
说说OC的消息机制?
1) OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
2)objc_msgSend分为三个阶段(消息发送、动态方法解析、消息转发)
2.1消息发送:
2.1.1 首先去该类的方法 cache中查找如果找到了就返它 , 2.12、如果没有找到,就去该类的方法列表中查找。如果在该类的方法列表中找到了,则将 IMP返回
2.1.3、如果在该类的方法列表中没找到对应的 IMP,在通过该类结构中的 super_class指针在其父类结构的方法列表中去查找,直到在某个父类的方法列表中找到对应的IMP,返回它,并加入cache中
2.14、如果在自身以及所有父类的方法列表中都没有找到对应的 IMP,则看是不是可以进行动态方法解析
2.2动态方法解析 ( 添加新方法)
如果是 对象方法调用会 调用_class_resoveInstanceMethod()如果是 类方法调用 调用 _class_resoveClassMethod()
2.3消息转发 (消息接收者重定向、消息重定向)
消息接收者重定向方法:
-(id)forwardTargetForSelector:(SEL) aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation
消息重定向:
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; //获取对象方法签名
-(void)forwardInvocation:(NSInvocation *)anInvocation //对象方法消息重定向
runtime具体应用?
1)分类添加方法 2)字典转模型 3)KVC/KVO/Block 4 ) 方法交换
method swizzling 方法交换原理: 首先讲一下SEL和IMP , SEL 是方法编号 IMP是函数指针地址 ,寻找IMP其实就是寻找函数的一个过程
每一个方法都有一个方法编号和函数指针地址,SEL通过对应表去查找IMP
原理就是 在运行时将一个方法的实现替换成另一个方法实现的过程! 简单来说就是将方法编号和函数地址原本对应的关系断开,将方法编号和新的函数地址进行绑定生成新的对应关系
方法交换需要注意的事项:
1> 交换方法应当在调用前完成交换,确保在调用时已经完成交换
2> 交换方法应放到dispathc_once中执行。防止手动调用+load 方法导致反复被交换
3> 交换的分类方法应该添加自定义前缀,避免冲突(分类的方法会覆盖类中同名的方法)
23. load 和initalize 区别
load: 在main函数之前通过函数内存地址调用。程序启动时候加载所有的类、每个类只调用一次。不需要继承父类实现。
initalize: 在main函数之后通过objc_msgSend收到消息时调用。
load和initialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。
24. 分类不能添加属性的原因
分类里添加属性,只是将该属性添加到该类的属性列表,并声明了setter和getter方法,但是没有生成相应的成员变量,也没有实现setter和getter方法。所以说分类不能添加属性 ,如果手动实现的话 改变内存的分布情况,这对编译性语言是灾难,是不允许的 。
分类只能扩展方法(属性仅仅是声明,并没真正实现)
25 事件的响应流程Fr
1.首先通过 hitTest:withEvent: 确定第一响应者,以及相应的响应链
2.判断第一响应者能否响应事件,如果第一响应者能进行响应则事件在响应链中的传递终止。如果第一响应者不能响应则将事件传递给 nextResponder也就是通常的superview进行事件响应
3.如果事件继续上报至UIWindow并且无法响应,它将会把事件继续上报给UIApplication
4.如果事件继续上报至UIApplication并且也无法响应,它将会将事件上报给其Delegate
5.如果最终事件依旧未被响应则会被系统抛弃
26 单元测试
介绍: 主要是xcode自带的XCTest. 简单应用场景就是(测试一些功能是否正常、代码的覆盖率等等,性能和逻辑的测试)例如要测试一个分享功能,避开重启APP进入分享界面、点击分享输入分享内容这一些列繁琐的操作。 测试很简单、主要是看自己架构设计
- [ setup ] 测试用例初始化方法 执行之前调用
- [ textXXX ] 要测试的实例方法,以text开头不含任何参数,测定预期值
- [ tearDown ] 清楚测试方法, 执行之后被调用
逻辑测试:testLogic 方法 里面 XCTAssertEqual 设定预期值验证
性能测试:testPrefromanceExample 方法 block里面做操作设置性能值选择代码通过率
异步测试:testAsync方法
UI测试: 这个是运行程序自动生成测试代码验证
27 KVC 简单分析
分2种: setValue: forKey(存值) 和 valueForKey(取值)
setValue: forKey(存值) 1> 通过setValue : forKey 方法按照 (setKey 、_setKey)顺序查找方法 找到直接传递参数调用方法
2> 没找到会调用 accessInstanceVariablesDirectly(是否允许查看成员变量)返回值NO 直接调用valueForUndefinedKey 方法抛出异常
3> 如果返回YES会按照(_key 、_isKey 、key 、isKey)查找成员变量 找到直接赋值 查不到直接抛出异常
valueForKey(取值) 1> 通过valueForKey方法按照 (getKey、key、isKey、key) 顺序查找方法 找到直接调用方法
2> 没找到会调用 accessInstanceVariablesDirectly查看返回值 NO 直接调用valueForUndefinedKey 方法抛出异常
3> 如果返回YES会按照(_key 、_isKey 、key 、isKey)查找成员变量 找到直接赋值 查不到直接抛出异常
29 button防止多次点击
1. 设置enabled或userInteractionEnabled属性
2. 借助cancelPreviousPerformRequestsWithTarget:selector:object实现
// 此方法会在连续点击按钮时取消之前的点击事件,从而只执行最后一次点击事件+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
// 多长时间后做某件事情- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
3. 通过runtime交换方法实现
{
1 创建一个UIButton的分类,使用runtime增加public属性cs_eventInterval和private属性cs_eventInvalid。
2 在+load方法中使用runtime将UIButton的-sendAction:to:forEvent:方法与自定义的cs_sendAction:to:forEvent:方法进行交换
3 使用cs_eventInterval作为控制cs_eventInvalid的计时因子,用cs_eventInvalid控制UIButton的event事件是否有效。
}
30 WKWebview
WKWebview的白屏的问题:
原因:1. WKWebview是一个多进程组件,内存占用太大的时候会在加载中奔溃导致白屏。2. 网络问题。
解决:1.清理缓冲 重新加载 iOS 9以后 WKNavigtionDelegate 新增了一个回调函数。-(void)webiewXXXTermi nate方法在即将出现白屏的时候回调用这个方法。里面执行[webview reload]
WKWebView设置自定义UserAgent:
1. WKWebView的customUserAgent会覆盖webview本身的userAgent;2.configuration.applicationNameForUserAgent设置的userAgent是拼接在webview本身的userAgent后面的。
正确设置自定义userAgentWKWebViewConfiguration *configuration = [WKWebViewConfiguration new];configuration.applicationNameForUserAgent = "iOS";_webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
使用方法:WKWebView来加载网页,使用WKWebViewConfiguration来配置JS交互。
js调用OC
1. 主要使用WKUserContentController,用来做 原生与JavaScript的交互管理
2.使用协议类WKScriptMessageHandler,用来处理监听JavaScript方法从而调用原生OC方法。
3. 通过 接收JS传出消息的name 进行捕捉的回调方法
OC调用JS
使用WKUserScript,执行自定义的JavaScript代码
31 MVVM
1.View主要用于界面呈现,与用户输入设备进行交互
2.ViewModel是MVVM架构中最重要的部分,ViewModel中包含属性,方法,事件,属性验证等逻辑,负责View与Model之间的通讯
3.Model就是我们常说的数据模型,用于数据的构造,数据的驱动,主要提供基础实体的属性。
MVVM主要目的是分离视图和模型MVVM优点:低耦合,可重用性,独立开发,可测试
32 什么时候会报unrecognized selector的异常?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,会进入消息转发阶段,如果消息三次转发流程仍未实现,则程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。
33 runtime如何通过selector找到对应的IMP地址?
每一个类对象中都一个方法列表,方法列表中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
34 objc在向一个对象发送消息时,发生了什么?
objc在向一个对象发送消息时,runtime会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果一直到根类还没找到,转向拦截调用,走消息转发机制,一旦找到 ,就去执行它的实现IMP 。
35 AutoreleasePool自动释放池
AutoreleasePool(自动释放池) 是OC中的一种内存自动回收机制,在释放池中的调用了autorelease方法的对象都会被压在该池的顶部(以栈的形式管理对象)。当自动释放池被销毁的时候,在该池中的对象会自动调用release方法来释放资源,销毁对象。以此来达到自动管理内存的目的
36 APP的启动
按照不同的阶段dyld: 减少动态库、合并一些动态库(定期清理不必要的动态库)减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)减少C++虚函数数量Swift尽量使用struct
runtime: 用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++静态构造器、ObjC的+load
main: 在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中按需加载
4. APP启动优化
1.减少数据的加载,最好使用懒加载
2.使用多线程技术加载数据,充分利用cpu的资源
3.启动时刻的界面,不要使用storyboard或xib,尽量使用纯代码,因为storyboard或xib有一步代码转换的操作,也是会耗时的
37 @synthesize/@dynamic
@synthesize 表示如果属性没有手动实现setter和getter方法,编译器会自动加上这两个方法。@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。
38 埋点
Flurry: 埋点类——方法列表部署
39 GCD
多线程: 提高CPU资源,实际往往是将耗时操作放在后台执行,避免阻塞主线程,在iOS中UI绘制和用户响应都是主线程
大三类: 1> NSThread 使用简单 缺点: 需要自己管理线程的生命周期
2> NSOperation 不需要关心线程管理,数据同步的事情 对于GCD的封装 相比GCD多了一些复杂功能(线程依赖、最大并发数设置等等)
3> CGD 用关心线程代码的实现, 即不需要关心什么时候开起线程, 关闭线程,只需要创建出来你要执行的任务, 然后把任务添加到适当的队列中(简单易用 )
常用线程锁: 1> synchronized 用着更方便,可读性更高
2> NSLock 必须保证上锁和开锁子同一线程 没有开锁前再上锁 会导致永久性死锁
3> dispatch_semaphore_t 只有上一个执行完才继续下一个 不然一直在等待(dispatch_semaphore_wait)
GCD会自动管理线程的生命周期, 比如创建线程, 调度任务, 销毁线程等等操作.
同步执行: 在GCD里是sync, 不会开启新线程, 只会在当前线程进行操作.
异步执行: 在GCD里是async, 可以另外开启一个新的线程执行任务.
并行队列: 全名为Concurrent Dispatch Queue, 指的是可以让多个任务同时执行, 如果用到并行队列的话, 是会自动开启多个线程同时执行任务
.串行队列: 全名Serial Dispatch Queue, 指的是任务一个接一个的执行, 完成了前面的那个就到后面那个, 和我们刚刚举的收费站例子一样.
使用方法:
这里可以使用dispatch_queue_create来创建对象, 这里需要传入两个参数
.第一个参数: 队列的唯一标识符
第二个参数: 队列的类型, DISPATCH_QUEUE_SERIAL表示串行队列,
DISPATCH_QUEUE_CONCURRENT表示并行队列.
常用方法:
dispatch_after 设置延迟在多少秒后执行
dispatch_get_global_queue 会获取一个全局队列,
dispatch_get_main_queue 会返回主队列
dispatch_once 一次性执行 只执行一次
40 单例
优点: 1.不用再频繁地创建和销毁对象,从而提高了系统的性能和节约系统资源
2. 单例对象可以做到按需创建对象或加载资源,以节省不必要的内存。
缺点: 1. 由于单利模式中没有抽象层接口, 单例类很难再进行扩展
2.单例对象长时间不被利用,系统有可能会认为是垃圾而被回收,这将导致当前单例对象状态的丢失。
41 TableView为什么不响应touchBegan
通过响应链我们不难想象到,当我们点击屏幕时,第一响应者应该是UITableView,而我们调用的touchBegan其实是ViewController的View的方法,所以无法被调用。
UITapGestureRecognizer添加手势实现方法作处理。 手势方法实现
/** 判断当前点击的位置是否处于 collectionView 对象内,如果是,则返回 NO 以使 UITapGestureRecognizer 手势对象失效
if ([touch.view isDescendantOfView:self.collectionView]) {
return NO;
}
return YES;
42 加密方式
MD5 base64 AES DES RSA(RSA (客户端)公钥 + 明文 = 密文 —> (服务器) 私钥 + 秘文 = 明文)
43 oc中的反射机制
反射机制,简单的说就是在程序运行期间通过类的名字来动态的获取类的信息,从而实现动态的创建类,以及动态的调用类的方法等。
常用到方法: 1> isKindOfClass : 该方法用于判断一个对象是不是一个类或者这个类的字类的实例对象
2> isMemberOfClass : 该方法用于判断一个对象是不是一个类的实例对象
3> respondsToSelector : 该方法用于判断一个对象是否实现了这个方法
动态调用方法: 1、[self performSelector:(id) withObject:(id)];2、objc_msgSend(self,SEL,parameters)等等
44 NSDictionary内部实现
NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的。hash函数设计的好坏影响着数据的查找访问效率,数据在hash表中分布的越均匀,其访问效率越高。在Objective-C中,通常都是利用NSString 来作为键值,其内部使用的hash函数也是通过使用 NSString对象作为键值来保证数据的各个节点在hash表中均匀分布。在调用 setObject: forKey: 后,内部会去调用 key 对象的 hash 方法确定 object 在hash表内的入口位置,然后会调用 isEqual 来确定该值是否已经存在于 NSDictionary中。
45 NSTimer不准的问题及解决
原因: 1、NSTimer被添加在mainRunLoop中,模式是NSDefaultRunLoopMode,mainRunLoop负责所有主线程事件,例如UI界面的操作,复杂的运算,这样就会造成timer的阻塞、
2、模式的切换,当创建的timer被加入到NSDefaultRunLoopMode时,此时如果有滑动UIScrollView的操作,runLoop 的mode会切换为TrackingRunLoopMode,这是timer会停止回调。
解决 1、 在子线程中创建timer,在子线程中进行定时任务的操作,需要UI操作时切换回主线程进行操作
2、 使用GCD定时器、GCD创建的定时器不受RunLoop中Modes影响 dispatch_source_set_timer(GCD dispatch_time_t 里面的都是纳秒)
46 内存管理
MRC: 手动管理内存(人工引用计数)当引用计数为0的时候,必须回收 继续释放,会造成野指针,为了避免出现野指针
ARC:自动管理内存(自动引用计数) 只要没有强指针(强引用)指向对象,对象就会被释放
ARC下需要注意: 循环引用问题(block 使用__weakSelf 代替self 、 代理使用weak 修饰属性)涉及到定时器、通知、单利等需要手动释放避免内存泄漏
一般会影响性能 严重导致程序崩溃 一般使用Xcode自带的Instruments工具中的leaks检测 标红的表示可能存在泄漏问题 查看具体解决
47 简单排序思路
快速排序: 通过一趟排序将数据分割成两部分,其中一部分的所有数据都比另一部分的所有数据都小,但是两部分数据是无序的。然后再对两部分的数据分别进行第一趟的排序,直到最后的数据是有序的每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。
选择排序: 是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
48 崩溃异常处理
一般使用三方Bugly、AvoidCrash
这个三方可以处理例如插入空值到字典中或数组中引起的崩溃、数组越界引起的崩溃、unrecognized selector sent to instance等等的崩溃,都能捕获并且避免闪退
也可以在它异常处理的方法下 选择上传服务器 对于线上出现的闪退 异常可以进行定位处理