iOS面试总结

1、修饰词:
当修饰可变类型的属性时,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong。
当修饰不可变类型的属性时,如NSArray、NSDictionary、NSString,用copy。
week修饰主要是为了解决循环引用,weak是弱引用。
例如:
1、在修饰代理属性的时候使用weak
2、连线创建控件用weak
3、block避免循环引用、controller强引用block,block代码块内又调用Controller,这时避免循环引用 __weak typeof(self) weakSelf = self;
Block用copy修饰
block声明默认是在栈上,随时可能会被销毁,所以被销毁后再次调用会造成系统崩溃。所以需要使用copy将block拷贝到堆区。copy操作只是做了次浅拷贝
block底层理解:定义一个 block 任务,实际是生成了一个指针,同时会生成一个包含该任务的静态函数,并且该指针指向该静态函数,而 block 的调用,则是使用该指针调用其所指向的静态函数。

2、通知、代理、block的区别:
通知,一对多,比如在开发中,很多控制器都想知道一个事件,所以用通知
通知的使用
代理:代理注重的是过程,是一对一的,对于一个协议就只能用一个代理

block:通常拿来OC中的block和swift中的闭包来比较.block注重的是过程,block会开辟内存,消耗比较大,delegate则不会,block防止循环引用,要用弱引用

3、讲一下atomic的实现机制;为什么不能保证绝对的线程安:

  • atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁
  • 这个锁只能保证单独的读或者单独的写诗线程安全,如果同时读和写,就不是线程安全。
  • 解决的方法就是读和写用同一把锁。

4、NSString用copy修饰的原因:

这是考虑到一个安全问题:为了确保被赋值的string不会被外界修改。
风险在于:如果一个NSMutableString类型的字符串,赋值给NSString类型的字符串,如果修饰词不是copy,而是strong,则NSString的值也可能会修改。
,因为用strong修饰,只是将NSString指向了NSMutableString所在的位置。
用copy修饰,在赋值时会把NSMutableString的内容复制一遍给NSString,此时两者指向的位置不同。
NSArray和NSDictionary用copy修饰的原因也一样。
但是NSMutableArray和NSMutableDictionary必须用strong修饰,因为用copy修饰类型会变成不可变数组或者不可变字典,新增数据时会崩溃。

5、线程:
GCD是面向底层的C语言的API,NSOpertaionQueue用GCD构建封装的,是GCD的高级抽象。
GCD和NSOperation区别
(1)NSOperation是基于cocoa框架实现的,GCD是基于C语言实现。
(2)NSOperation实现多线程更加面向对象,使用者只需要更多的关注操作本身的处理即可,GCD更加抽象,代码简洁
(3)NSOperation可以通过KVO监控操作进行的状态(准备、执行中、完成、被取消),GCD没有。
(4)NSOperation可以很容易管理各个操作之间的依赖关系,GCD可以通过block嵌套实现,较为复杂
(5)NSOperationQueue可以设置最大并发数,GCD可以通过信号量控制,很麻烦
3、NSOperationQueue和GCD有什么区别?
(1) GCD底层是C语言构成的API。NSOperationQueue及相关对象是Objc对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构。而NSOperation作为一个对象,为我们提供了更多的选择。
(2) 在NSOperationQueue中,取消任务非常方便,而GCD没法停止已经加入queue的block。
(3) NSOperation能够方便的设置依赖关系,还能设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行。在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务优先级也需要大量复杂代码。
NSOperationQueue还可以设置最大并发数,GCD则需要自己实现。
(4)NSOperation任务状态属性支持KVO,可以通过KVO来监听operation的就绪、取消、执行中、执行完成等状态。GCD则无法判断当前任务执行状态。
发生线程死锁的条件是,在队列里面的任务没有执行完毕的时候,在同一个队列里面添加了同步执行的任务.
死锁是由于队列阻塞而非线程原因引起的。因为两个任务都是同步任务,都在同一个线程上执行

1.串行队列+同步任务:不会开启新的线程,任务逐步完成。
2.串行队列+异步任务:开启新的线程,任务逐步完成。
3.并发队列+同步任务:不会开启新的线程,任务逐步完成。
4.并发队列+异步任务:开启新的线程,任务同步完成。

6、kvc kvo 对比:

  • KVO 是通过 isa-swizzling的技术实现的
  • 当为对象的属性添加观察者时,会修改观察对象的isa 指针,指向一个中间类,此时 isa 指针的值不一定反映对象的实际类
  • 在注册KVO 观察者后,观察对象的isa 指针会发生改变,指向了一个中间类。
  • 在移除 KVO 观察者之后,isa 的指向又从NSKVONotifying_Person变成了 Person。
    KVC: 通过Key名直接访问对象的属性,或者给对象的属性赋值.

7、代理,通知,block:
NSNotificationCenter 特点:“一对多”,“多对一”关系 观察者对象在释放前一定要将通知中心注销掉,否则将出现不可预见的crash
优点:使用简单,解决了同时向多个对象监听相应的问题,传值方便快捷
缺点:无法检测到通知中心是否正在处理接受的消息;调试时很难跟踪动作;消息发送者在发送消息的时候不能获取任何反馈信息等

代理 特点:“一对一” 公共接口、方法较多选用delegate进行解耦如,AFNetworking等;UI事件响应,如UITableViewDelete,UITextViewDelete等减少代码耦合,使事件监听和事件处理相分离;语法定义清晰,减少维护成本,代码可读性较强等

缺点:实现委托的代码过程比较繁琐;跨层传值监听将会加大代码的耦合性,并且使程序的层次结构变得混乱;当多个对象同时传值响应时,delegate的易用性大大降低;多弹窗时代理需要区别来源。

block是一个能够访问其他函数内部变量的函数,特点:"一对一"关系;注重结果,只关注是否成功。注意:防止循环引用,优点:语法简洁,代码紧凑;增强代码的可读性和可维护性;配合GCD可以很好地解决多线程问题;多弹窗时,block在创建时就区分了来源;

8、数据存储:
NSUserDefaults,Archive,plist存储方式,数据库存储

9、网络编程 TCP,UDP :
http与https是应用层协议,他们建立在传输层的tcp协议上。http协议默认端口是80,https默认端口443。

链接:https://www.runoob.com/w3cnote/http-vs-https.html

image.png

Get请求参数是以kv方式拼在url后面的,虽然http协议对url的长度没有限制,但是浏览器和服务器一般都限制长度;Post请求参数是放在body里面的,对长度没什么限制。

10、AFNetworking实现原理:
https://www.jianshu.com/p/cc9c662d2bc5

11、VC控制器的生命周期:
①,initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
②,loadView:加载view
③,viewDidLoad:view加载完毕
④,viewWillAppear:控制器的view将要显示
⑤,viewWillLayoutSubviews:控制器的view将要布局子控件
⑥,viewDidLayoutSubviews:控制器的view布局子控件完成
这期间系统可能会多次调用viewWillLayoutSubviews 、 viewDidLayoutSubviews 俩个方法
⑦,viewDidAppear:控制器的view完全显示
⑧,viewWillDisappear:控制器的view即将消失的时候
这期间系统也会调用viewWillLayoutSubviews 、viewDidLayoutSubviews 两个方法
⑨,viewDidDisappear:控制器的view完全消失的时候
⑩,didReceiveMemoryWarning(内存满时)

12、App网络层有哪些优化策略?
1、优化DNS解析和缓存
2、网络质量检测(根据网络质量来改变策略)
3、提供网络服务优先级和依赖机制
4、提供网络服务重发机制
5、减少数据传输量
6、优化海外网络性能
7、购买配置CDN网络加速服务

13、iOS开发中的加密方式

  • base64加密
  • Token值加密:服务端生成token,客户端保存后每次请求发送token到服务端,用来判断登陆状态。
  • MD5加密--(信息-摘要算法) 哈希算法之一、
  • 时间戳密码以及指纹识、人脸识别
  • keychain保存

14、WebSocket与TCP Socket的区别

  • WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。
  • TCP Socket不是协议,是一套API,是对TCP、UDP复杂传输方法的一套封装。位于应用层和传输层之间。

15、事件的传递

  • 触摸事件的传递是从父控件传递到子控件
  • 也就是UIApplication->window->寻找处理事件最合适的view
  • 注 意: 如果父控件不能接受触摸事件,那么子控件就不可能接收到触摸事件

16、app优化:
一、启动优化
冷启动

Main函数执行前

  • 减少动态库加载。每个库本身都有依赖关系,苹果公司建议使用更少的动态库,并且建议在使用动态库的数量较多时,尽量将多个动态库进行合并。数量上,苹果公司建议最多使用 6 个非系统动态库。

  • 减少加载启动后不会去使用的类或者方法

  • +load() 方法里的内容可以放到首屏渲染完成后再执行,或使用 +initialize() 方法替换掉。 因为,在一个+load() 方法里,进行运行时方法替换操作会带来 4 毫秒的消耗。不要小看这 4 毫秒,积少成多,执行 +load() 方法对启动速度的影响会越来越大

  • 控制 C++ 全局变量的数量
    Main函数执行后

  • 功能级别优化
    main() 函数开始执行后到首屏渲染完成前只处理首屏相关的业务,其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做

  • 方法级别去优化
    我们需要进一步做的,是检查首屏渲染完成前主线程上有哪些耗时方法,将没必要的耗时方法滞后或者异步执行。通常情况下,耗时较长的方法主要发生在计算大量数据的情况下,具体的表现就是加载、编辑、存储图片和文件等资源

APP启动的监控手段

  • 1、定时抓取主线程上的方法调用堆栈,计算一段时间里各个方法的耗时
  • 2、对 objc_msgSend 方法进行 hook 来掌握所有方法的执行耗时。

17、卡顿问题:
2.1 CPU和GPU
2.2 离屏渲染(尽量避免出现离屏渲染)

CUP(Central processing Unit,中央处理器)

  • 对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)
    GPU (Graphics Processing Unit,图形处理器)
  • 纹理的渲染
    可以优化的点
  1. 尽可能减少CPU、GPU资源消耗
  2. 尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView
  3. 不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改
  4. 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性
  5. Autolayout会比直接设置frame消耗更多的CPU资源
  6. 图片的size最好刚好跟UIImageView的size保持一致
  7. 控制一下线程的最大并发数量
  8. 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示
  9. GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸
  10. 尽量减少视图数量和层次
  11. 减少透明的视图(alpha<1),不透明的就设置opaque为YES
  12. 尽量把耗时的操作放到子线程(文本尺寸、图片处理)

离屏渲染(尽量避免出现离屏渲染)

  • 在OpenGL中,GPU有2种渲染方式
  1. On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
  2. Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
    2.3 离屏渲染消耗性能的原因
  • 1、需要创建新的缓冲区
  • 2、离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕
    2.4 哪些操作会触发离屏渲染?
  • 光栅化,layer.shouldRasterize = YES
  • 遮罩,layer.mask
  • 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0(考虑通过CoreGraphics绘制裁剪圆角,或者叫UI提供圆角图片)
  • 阴影,layer.shadowXXX (如果设置了layer.shadowPath就不会产生离屏渲染)

18、耗电优化

  • 1、尽可能降低CPU、GPU功耗
  • 2、少用定时器
  • 3、优化I/O操作
  • 4、尽量不要频繁写入小数据,最好批量一次性写入
  • 5、读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispatch_io系统会优化磁盘访问
  • 6、数据量比较大的,建议使用数据库(比如SQLite、CoreData)

19、网络优化

  • 1、减少、压缩网络数据
  • 2、如果多次请求的结果是相同的,尽量使用缓存
  • 3、使用断点续传,否则网络不稳定时可能多次传输相同的内容
  • 4、网络不可用时,不要尝试执行网络请求
  • 5、让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
  • 6、批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一 次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载

20、定位优化

  • 1、如果只是需要快速确定用户位置,最好用CLLocationManager的requestLocation方法。定位完成后,会自动让定位硬件断电
  • 2、如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务
  • 3、尽量降低定位精度,比如尽量不要使用精度最高的kCLLocationAccuracyBest
  • 4、需要后台定位时,尽量设置pausesLocationUpdatesAutomatically为YES,如果用户不太可能移动的时候系统会自动暂停位置更新
  • 5、尽量不要使用startMonitoringSignificantLocationChanges,优先考虑startMonitoringForRegion:
  • 6、用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件

21、包大小优化
1、删除无用图片资源2、图片资源压缩3、代码瘦身

22、runtime:
Runtime是iOS核心运行机制之一,iOS App加载库、加载类、执行方法调用,全靠Runtime
1)iOS调用一个方法时,实际上会调用objc_msgSend(receiver, selector, arg1, arg2, ...),该方法第一个参数是消息接收者,第二个参数是方法名,剩下的参数是方法参数; 2)iOS调用一个方法时,会先去该类的方法缓存列表里面查找是否有该方法,如果有直接调用,否则走第3)步; 3)去该类的方法列表里面找,找到直接调用,把方法加入缓存列表;否则走第4)步; 4)沿着该类的继承链继续查找,找到直接调用,把方法加入缓存列表;否则消息转发流程
.runtime有什么作用?
1.能动态产生一个类,一个成员变量,一个方法
2.能动态修改一个类,一个成员变量,一个方法
3.能动态删除一个类,一个成员变量,一个方法

23、runloop
RunLoop是线程基础设施的一部分。RunLoop是iOS中用来接受事件、处理事件的循环。
RunLoop与线程是一一对应关系,一个线程对应一个RunLoop,他们的映射存储在一个字典里,key为线程,value为RunLoop。
RunLoop内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。RunLoop t通过调用mach_msg函数进入休眠等待唤醒状态。
RunLoop应用:AutoreleasePool、事件响应、手势识别、界面更新、定时器、PerformSelecter、GCD、网络请求底层等都用到了RunLoop

解决NSTimer事件在列表滚动时不执行问题:因为定时器默认是运行在NSDefaultRunLoopMode,在列表滚动时候,主线程会切换到UITrackingRunLoopMode,导致定时器回调得不到执行。 有两种解决方案:

  • 指定NSTimer运行于 NSRunLoopCommonModes下。
  • 在子线程创建和处理Timer事件,然后在主线程更新 UI。

24、mvc mvvm
无论是MVC还是MVVM,我们的本质都是想对VC进行瘦身。 用MVVM的话,分层更加清晰。
MVC:

  • 在初始化时,构造相应的 View 和 Model。
  • 监听 Model 层的事件,将 Model 层的数据传递到 View 层。
  • 监听 View 层的事件,并且将 View 层的事件转发到 Model 层。
  • 其他的事情可以由若干个Manager来完成。
    每个业务模块至少建立Model、View、VC、Network、Cache五个文件夹来分类代码,并且遵循上面原则来给VC瘦身。
  • 网络层。定义网络请求类 ,一个网络请求代表一个类。网络请求类负责发网络请求以及把响应数据解析为model,model以及response通过block方式回调给调用方。这里的调用方大部分时候是VC。
  • 数据存储层。定义数据存储Manager,用来加载数据和缓存数据。
  • 定义数据转换层。负责数据转换,比如Model跟View Model的转换。
  • 可以通过类别的方式给VC做好功能的划分,比如定义TableView分类用来处理UITableViewDelegate && UITableViewDataSource代理方法;定义Network分类用来处理网络逻辑。
    MVVM:
  • View、VC可以持有View Model,反之不行;View Model可以持有Model,反之不行。 View Model相当于MVC中的VC作用,用来连接View、VC 和Model。
  • 当Model更新时,通知View Model,View Model再通知VC或者View,来更新View;
  • 当用户点击View时候,通知View Model,View Model通知Model来更新Model。
  • 实现MVVM的关键是如何做数据通知,在iOS中可以用KVO来实现。业内一般用ReactiveCocoa来实现数据绑定及通知

25、队列与栈有什么区别?
栈:限定仅在表尾进行插入或删除操作的线性表。表尾是栈顶,表头是栈底。它又称后进先出线性表。
队列:是一种先进先出的线性表。它只允许在表的一端进行插入,而在另一端删除元素。

26、Objective-C运行时(runtime)机制了解吗?简单的说说对象调用方法的过程。
对象调用方法的过程:(Objective-C Runtime)
(1)在对象类的dispatch table中尝试找到该消息。如果找到了,跳到相应的函数IMP去执行实现代码
(2)如果没有找到,Runtime会发送+resolveInstanceMethod: 或者 +resolveClassMethod: 尝试去resolve这个消息
(3)如果resolve方法返回NO,Runtime就发送 -forwardingTargetForSelector: 允许你把这个消息转发给另一个对象;
(4)如果没有新的目标对象返回,Runtime 就会发送 -methodSignatureForSelector: 和 -forwardInvocation: 消息。你可以发送 -invokeWithTarget: 消息来手动转发消息或者发送 -doesNotRecognizeSelector: 抛出异常。

27、对称加密和非对称加密的区别?分别有哪些算法的实现?
答:

  1. 对称加密:使用同一个秘钥,非对称加密使用2把密钥加密和解密,即公钥、私钥
  2. 常见的加密方式:
    1. 不可逆
      • 单向散列函数:MD5、SHA
    2. 可逆
      • 对称加密:DES、3DES、AES
      • 非对称加密:RSA
    3. 其他
      • 混合密码系统
      • 数字签名
      • 证书

28、简述一下Dealloc的实现机制

  • 当一个对象要释放时,会自动调用dealloc,接下的调用轨迹是
  1. dealloc
  2. _objc_rootDealloc
  3. rootDealloc
  4. object_dispose
  5. objc_destructInstance、free

29、遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?
1.常见的就是cell的复用,注册重用标识符
如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell;
如果有很多数据的时候,就会堆积很多cell。
如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell。
2.避免cell的重新布局
cell的布局填充等操作 比较耗时,一般创建时就布局好。如可以将cell单独放到一个自定义类,初始化时就布局好。
3.提前计算并缓存cell的属性及内容
当我们创建cell的数据源方法时,编译器并不是先创建cell 再定cell的高度,而是先根据内容一次确定每一个cell的高度,高度确定后,再创建要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提前估算高度告诉编译器,编译器知道高度后,紧接着就会创建cell,这时再调用高度的具体计算方法,这样可以方式浪费时间去计算显示以外的cell。
4.减少cell中控件的数量
尽量使cell得布局大致相同,不同风格的cell可以使用不用的重用标识符,初始化时添加控件,不适用的可以先隐藏。
5.不要使用ClearColor,无背景色,透明度也不要设置为0
渲染耗时比较长。
6.使用局部更新
如果只是更新某组的话,使用reloadSection进行局部更新。
7.加载网络数据,下载图片,使用异步加载,并缓存
8.少使用addView 给cell动态添加view
9.按需加载cell,cell滚动很快时,只加载范围内的cell
10.不要实现无用的代理方法,tableView只遵守两个协议
11.缓存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这两者同时存在才会出现“窜动”的bug。
所以我的建议是:只要是固定行高就写预估行高来减少行高调用次数提升性能。如果是动态行高就不要写预估方法了,用一个行高的缓存字典来减少代码的调用次数即可
12.不要做多余的绘制工作。
在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。
例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。
13.预渲染图像。
当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;
14.使用正确的数据结构来存储数据。

30、如何提升 tableview 的流畅度?
1.提前计算并缓存好高度,因为heightForRow最频繁的调用。
2.异步绘制,遇到复杂界面,性能瓶颈时,可能是突破口。
3.滑动时按需加载,这个在大量图片展示,网络加载时,很管用。(SDWebImage已经实现异步加载)。
4.重用cells。
5.如果cell内显示得内容来自web,使用异步加载,缓存结果请求。
6.少用或不用透明图层,使用不透明视图。
7.尽量使所有的view opaque,包括cell本身。
8.减少subViews
9.少用addView给cell动态添加view,可以初始化的时候就添加,然后通过hide控制是否显示。

31、APP启动时间应从哪些方面优化?
App启动时间能够经过xcode提供的工具来度量,在Xcode的Product->Scheme-->Edit Scheme->Run->Auguments中,将环境变量DYLD_PRINT_STATISTICS设为YES,优化需如下方面入手

  • dylib loading time
    核心思想是减小dylibs的引用
    合并现有的dylibs(最好是6个之内)
    使用静态库
  • rebase/binding time
    核心思想是减小DATA块内的指针
    减小Object C元数据量,减小Objc类数量,减小实例变量和函数(与面向对象设计思想冲突)
    减小c++虚函数
    多使用Swift结构体(推荐使用swift)
  • ObjC setup time
    核心思想同上,这部份内容基本上在上一阶段优化事后就不会太过耗时
    initializer time
  • 使用initialize替代load方法
    减小使用c/c++的attribute((constructor));推荐使用dispatch_once() pthread_once() std:once()等方法
    推荐使用swift
    不要在初始化中调用dlopen()方法,由于加载过程是单线程,无锁,若是调用dlopen则会变成多线程,会开启锁的消耗,同时有可能死锁
    不要在初始化中建立线程

32、如何检测离屏渲染与优化

  • 检测,经过勾选Xcode的Debug->View Debugging-->Rendering->Run->Color Offscreen-Rendered Yellow项。
  • 优化,如阴影,在绘制时添加阴影的路径

33、怎么检测图层混合
一、模拟器debug中color blended layers红色区域表示图层发生了混合
二、Instrument-选中Core Animation-勾选Color Blended Layers
避免图层混合:

  • 确保控件的opaque属性设置为true,确保backgroundColor和父视图颜色一致且不透明
  • 如无特殊须要,不要设置低于1的alpha值
  • 确保UIImage没有alpha通道
    UILabel图层混合解决方法:
    iOS8之后设置背景色为非透明色而且设置label.layer.masksToBounds=YES让label只会渲染她的实际size区域,就能解决UILabel的图层混合问题
    iOS8 以前只要设置背景色为非透明的就行
    为何设置了背景色可是在iOS8上仍然出现了图层混合呢?
    UILabel在iOS8先后的变化,在iOS8之前,UILabel使用的是CALayer做为底图层,而在iOS8开始,UILabel的底图层变成了_UILabelLayer,绘制文本也有所改变。在背景色的四周多了一圈透明的边,而这一圈透明的边明显超出了图层的矩形区域,设置图层的masksToBounds为YES时,图层将会沿着Bounds进行裁剪 图层混合问题解决了

34、平常如何检查内存泄露?

  • 目前我知道的方式有如下几种
    Memory Leaks
    Alloctions
    Analyse
    Debug Memory Graph
    MLeaksFinder
  • 泄露的内存主要有如下两种:
    Laek Memory 这种是忘记 Release 操做所泄露的内存。
    Abandon Memory 这种是循环引用,没法释放掉的内存

35、objc 使用什么机制管理对象内存?
用的是引用计数的机制。通过 retainCount 的机制来决定对象是否需要释放。每次 run loop 的时候,都会检查对象的 retainCount,如果 retainCount 为 0,说明该对象没有地方需要继续使用了,可以释放掉了。

36、常见崩溃的类型
APP的崩溃可以分为两类:信号可捕捉崩溃 和 信号不可捕捉崩溃。
信号可捕捉的崩溃

  • 数组越界:取数据时候索引越界,APP发生崩溃。给数组添加nil会崩溃。

  • 多线程问题:多个线程进行数据的存取,可能会崩溃。例如有一个线程在置空数据的同时另一个线程在读取数据。

  • 野指针问题:指针指向一个已删除的对象访问内存区域时,会出现野指针崩溃。野指针问题是导致 App 崩溃的最常见,也是最难定位的一种情况。

  • NSNotification线程问题:NSNotification 有很多种线程实现方式,同步、异步、聚合,所以不恰当的线程发送和接收会出现崩溃问题。

  • KVO问题:‘If your app targets iOS 9.0 and later or OS X v10.11 and later, you don't need to unregister an observer in its deallocation method。’ 在9.0之前需要手动remove 观察者,如果没有移除会出现观察者崩溃情况。
    信号不可捕捉的崩溃

  • 后台任务超时

  • App超过系统限制的内存大小被杀死

  • 主线程卡顿被杀死

37、设计模式主要有以下几种
1.MVC模式
2.单例模式
原则:单一职责原则

  • 声明一个可以新建和获取单个实例对象的方法
  • 声明一个static类型的类变量
  • 声明一个只执行一次的任务
  • 调用dispatch_once执行该任务指定的代码块,在该代码块中实例化上文声明的类变量
  • 返回在整个应用的生命周期中只会被实例化一次的变量
    以上就是iOS开发中单例模式的机制,

3.代理模式
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。
自定义的delegate

4.观察者模式(一般分为:通知和KVO)
通知(notification)机制:Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。

5.策略模式
6.工厂模式

38、Swift和OC的区别
1.swift是静态语言,有类型推断,OC是动态语言。
2.swift面向协议编程,OC面向对象编程
3.swift注重值类型,OC注重引用类型。
4.swift支持泛型,OC只支持轻量泛型
5.swift支持静态派发(效率高)、动态派发(函数表派发、消息派发)方式,OC支持动态派发(消息派发)方式。
6.swift支持函数式编程
7.swift的协议不仅可以被类实现,也可以被struct和enum实现
8.swift有元组类型、支持运算符重载
9.swift支持命名空间
10.swift支持默认参数
11.swift比oc代码更加简洁

39、String是结构体,NSString是类,这是它们的根本区别
1、NSString (class):引用类型,使用isa指针指向,分配在堆中
2、String (struct):值类型,赋值传递的值,分配在栈中
3、NSString (class)允许被继承,String (struct)不允许被继承

40、内存管理 :
引用计数机制,一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程,分为mrc(手动) arc(自动)
OC类中实现了引用计数器,对象知道自己当前被引用的次数。
对象初始化时计数器为1,每次操作对象都会引起相应的计数器变化;
retain+1,release-1;
当引用计数为0时,给对象发送dealloc消息销毁对象。

MRC 需要引用对象时,发送retain消息,对象的引用计数+1;

不需要引用对象时,发送release消息,对象引用计数-1;

当引用计数为0时,自动调用对象的dealloc方法销毁对象,释放内存;
引用计数为0的对象,不能再使用release和其他方法。

ARC也是基于引用计数,只是编译器在编译时期自动在已有代码中插入合适的内存管理代码(包括retain、release、copy、autorelease、autoreleasepool)以及在运行时做了一些优化。
简单来说,就是代码中自动加入了retain、release,原先MRC中需要手动添加的用来管理引用计数的代码都由编译器帮我们完成了

retain:持有,对原对象引用计数加1,强引用。ARC中使用strong。

copy:拷贝,复制一个对象并创建strong关联,引用计数为1 ,原来对象计数不变。

assign:赋值,不涉及引用计数的变化,弱引用。ARC中对象不使用assign,但原始类型(BOOL、int、float)仍然可以使用,assign修饰的对象,当对象释放之后,即引用计数为0时,指针变量并不会同时置为nil,全局变量就是变为野指针,不知道指向哪,再向该对象发消息,非常容易崩溃。
因此,当属性类型是对象时,不要使用assign,会带来一些风险。

weak:赋值(ARC),比assign多了一个功能,对象释放后把指针置为nil,避免了野指针,weak只能用来修饰对象,不能用来修饰基本数据类型。

strong:持有(ARC),等同于retain。

在你打开ARC时,你是不能使用retain、release、autorelease 操作的,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了,但是你需要在对象属性上使用weak 和strong, 其中strong就相当于retain属性,而weak相当于assign,基础类型只需声明非原子锁即可。
 僵尸对象:内存已经被回收的对象。
 野指针:指向僵尸对象的指针,向野指针发送消息会导致崩溃
空指针:是指没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错

41、事件传递链与响应链
用户点击屏幕时,首先UIApplication对象先收到该点击事件,再依次传递给它上面的所有子view,直到传递到最上层,
即UIApplication ——> UIWindow ——> RootViewController ——> View ——> Button,即传递链。
而反之Button ——> View ——> RootViewController ——> UIWindow ——> UIApplication则称为响应链。

42、iOS 引起循环引用的四种情况
一、block
一般情况下,我们使用copy修饰block,但copy的作用只是将block从栈区拷贝到堆区,并不是弱引用,而是强引用(copy修饰不可变对象时,相当于strong)。
所以self对block进行了强引用,那么block便不能强引用self。所以
解决方案就是,在block中使用weakself
二、delegate
可以用weak修饰delegate。
这是为了避免对象及其委托之间产生强引用循环,例如,BNRHypnosisViewController 是 UITextField 对象的委托,而且UITextField对象是BNRHypnosisViewController的强引用属性,如果UITextField对象再对其委托保持强引用,就会在两者之间产生强引用循环,很可能造成内存泄漏。
三、NSTimer
1⃣️首先我们声明timer的时候,使用strong修饰的,self对timer进行的强引用,如下
@property (nonatomic, strong) NSTimer *timer;

2⃣️创建timer时,target对self又进行了强引用

  • (NSTimer *)timer {
    if (_timer == nil) {
    _timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(setTime) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    }
    return _timer;
    }
    至此,循环引用已经形成。如果想控制器正常退出销毁,我们必须在退出之前销毁timer。
    解决方案:在viewWillDisappear中销毁timer
  • (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.timer invalidate];
    self.timer = nil;
    }
    这样,控制器就可以正常退出了。
    注意:如果我们没有将timer销毁,控制器也会执行dealloc方法,但是timer还会继续执行
    四、通知
    添加观察者和移除观察者,要成对出现。
    如果只添加了观察者,没有移除观察者,会引起循环引用,导致控制器不能释放。

43、各种锁的使用总结
(1).自旋锁:是用于多线程同步的一种锁,线程反复检查锁变量是否可用

(2).互斥锁(Mutex):是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。

(3).读写锁:是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。

(4).条件锁:就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。

(5).递归锁:递归锁有一个特点,就是同一个线程可以加锁N次而不会引发死锁。

44、iOS与JS交互的方法:
1.拦截url(适用于UIWebView和WKWebView)
2.JavaScriptCore(只适用于UIWebView,iOS7+)
3.WKScriptMessageHandler(只适用于WKWebView,iOS8+)
4.WebViewJavascriptBridge(适用于UIWebView和WKWebView,属于第三方框架)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容

  • 最近刚经历完苦逼的面试,抓紧吧技术面试题小结一下 1.tableView如何优化? >1.cell重用 >2....
    Esan同学阅读 801评论 2 33
  • 2017年iOS面试总结 1、为什么说Objective-C是一门动态语言? 答:Objective-C类的类型和...
    我叫王可可阅读 332评论 0 1
  • iOS面试总结 1. 网络 HTTP协议(HyperText Transfer Protocol)的请求和响应请求...
    崇山峻岭阅读 611评论 0 2
  • 1、简述__kindof关键字 如果想保证数组中只能存在某一类元素,这就需要添加泛型,比如@property (n...
    CrazySnow阅读 696评论 0 6
  • 我是一只勤劳的小蜜蜂。闲暇之余,总结下一直以来自己面试过程中所遇到的一些高概率问题,分享给大家,共同成长。该贴后续...
    智障小鲁班阅读 444评论 0 12