UIButton 与 UITableView 的层级结构
- 继承结构,属于内部的子控件结构
- UIButton为:UIButton > UIControl > UIView > UIResponder > NSObject
- UITableView为:UITableView > UIScrollView > UIView > UIResponder > NSObject
设置 ScrollView 的 contentSize 能在 ViewDidLoad 里设置吗?为什么?
- 一般情况是可以在 ViewDidLoad 里设置,但是在 autolayout 下,系统会在 ViewDidAppear之前根据 subview 的 constraint 重新计算scrollview 的 contentsize。这就是为什么,在 Viewdidload 里面手动设置了 contentSize 没用。因为在后面,会再重新计算一次,前面手动设置的值会被覆盖掉。
- 解决办法就是:
- 去除 autolayout 选项,自己手动设置 contentsize
- 如果要使用 autolayout,那么自己设置完 subview 的 constraint,然后让系统自动根据 constraint 计算出 contentsize。要么就在 Viewdidappear 里自己手动设置 contentsize
简述一下你对 UIView、UIWindow 和 CALayer 的理解
- UIView:属于 UIKit.framework 框架,负责渲染矩形区域内容,为矩形区添加动画,响应区域的触摸事件,布局和管理一个或多个子视图。
- UIWindow:属于 UIKit.framework 框架,是一个特殊的 UIView,通常在一个程序只会有一个 UIWindow,但可以手动创建多个 UIWindow,同时加到程序里去。UIWindow 在程序中主要起3个作用:
- 作为容器,包含 app 所要显示的所有视图
- 传递触摸消息到程序的 View 和其他对象
- 与 UIViewController 协同工作,方便完成设备方向旋转的支持
- CALayer:属于 QuartzCore.framework,是用来绘制内容的,对内容进行动画处理依赖与 UIView 来进行显示,不能处理用户事件。
- UIView 和 CALayer 是相互依赖的,UIView 依赖 CALayer 提供内容,CALayer 依赖 UIView 的容器显示绘制内容。
- UIViewController:每个视图控制器都有一个自带的视图,并且负责这个视图相关的一切事务。方便管理视图中的子视图,负责 Model 与 View 之间的通信,检测设备旋转以及内存警告;是所有视图控制类的积累,定义了控制器的基本功能
** frame 和 bounds 有什么不同?**
- frame 指的是:该 View 在父View 坐标系统中的位置和大小(参照是父类的坐标系统)
- bounds 指的是:该 View 在本身坐标系统中的位置和大小(参照点事本身坐标系统)
关于视图的声明周期的问题
- 首先判断控制器是否有视图,如果没有通过 loadview 方法创建:通过 storyboard 或者代码
- 随后调用 Viewdidload,可以进行下一步的初始化操作,只会被调用一次
- 在视图显示之前调用 Viewwillappear,该函数可多次调用
- 在布局变化后,调用 Viewwill、didLayoutSubviews 处理相关信息
响应者链条?
- 事件响应链。包括点击事件,画面刷新等。在视图栈内从上至下,或者从下至上传播,可以说点事件的分发,传递以及处理。
- 响应者链
- UIResponder 类是 UIKit 中一个用于处理事件响应的基类。窗口上的所有事件触发,都由该类响应(即事件处理入口)。所以,窗口上的 View 及其控制器都是派生于该类的,例如 UIView、UIViewController
- 调用 UIResponder 类提供的方法或者属性,我们就可以捕捉到窗口上的所有响应事件,并进行处理。
- 响应者链条是由多个响应者对象连起来的链条,其中响应者对象是能处理事件的对象,所有的 View 和 ViewController 都是响应者对象,利用响应者链条能让多个控件处理同一个触摸事件。
- 事件传递机制
- 如果当前 View 不能处理当前事件,那么事件将会沿着响应链进行传递,直到遇到能处理该事件的响应者。
UITableView 的重用机制?
- 查看 UITableView 头文件,会找到 NSMutableArray visiableCells,和 NSMutableArray reusableTableCell 两个结构
- visiableCells内保存当前显示的 cells,reusableTableCell保存可重用的 cells
- tableView 显示之初,reusableTableCell为空,那么 [tableView dequeueReusabelCellWithIdentifier:cellIdentifier]返回 nil;
- 开始的 cell 都是通过 init 来创建,而且 cellForRowAtIndexPath 只是调用最大显示 cell 数的次数。比如:有100条数据,iPhone 一屏最多显示10个 Cell
- 程序最开始显示 tableview 的情况是:
- 用init 创建10个 cell,并给 cell 指定同样的重用标识(当然,可以为不同显示类型的 cell指定不同标识)。并且10个 cell 全部都加入到 visiableCells 数组,reusableTableCells 为空。
- 向下拖动 tableview,当 cell 完全移除屏幕,并且 cell11(他也是 alloc 出来的,原因用上)完全显示出来的时候。cell11加入到 visiableCells,cell11移除 visiableCells,cell11 加入到 reusableTableCells。
- 接着向下拖动 tableview,因为 reusableTableCells 中已经有值,所以当需要显示新的 cell,cellForRowAtIndexPath 再次调用的时候,[tableView dequeueReusabelCellWithIdentifier:cellIdentifier]返回 cell。cell1加入到 visiableCells,cell1移除 reusableTableCells;cell2移除 visiableCells,cell2加入到 reusableTableCells。之后再需要显示的 cell 就可以正常重用了。
- 注意:配置 cell 的时候一定要注意,对取出的重用 cell 做重新赋值,不要遗留老数据。
UITableView 的性能优化?滑动的时候有种卡的感觉是为什么?怎么解决?
在使用第三方应用时,却经常遇到性能上的问题,普遍表现在滑动时比较卡,特别是 cell 中包含图片的情况时。
-
实际上针对性的优化就可以解决 tableview 滑动的时候卡顿的问题:
- 使用不透明的视图。不透明视图可以提高渲染的速度。可以将 cell 及其子视图的 opaque 属性设置为 YES(默认值)。
- 不要重复创建不必要的 cell。UITableView 只需要一屏幕的 UITableViewCell 的对象即可。因此在 cell 不见的时,可以将其缓存起来,而在需要时继续使用它即可。注意:cell 被重用时,需要调用 setNeedsDisplayInRect:或 setNeedsDisplay 方法重绘cell。
- 减少动画效果的使用,最好不要使用 insertRowsAtIndexPath:withRowAnimation:方法,而是直接调用 reloadData 方法。
- 减少视图的数目。cell 包含了 textLabel、detailTextLabel 和 imageView 等 View,而你还可以自定义一些视图放在它contextView 里,创建它会消耗较多的资源,并且也影响渲染的性能。
- cell 包含图片,且数目较多,使用自定义 cell 速度比默认的要快。继承 UITableViewCell,重写 drawRect 方法:不过这样以来,你会发现选中一行后,这个 cell 就变蓝了,其中的内容就被遮挡住了。最简单的办法就是将 cell 的 selectionStyle 属性设置为 None,这样就不会高亮了。不需要与用户交互的时候,使用 CALayer,将内容绘制到 layer 上,然后对 cell 的 contextView.layer 调用 addSubLayer方法。这个例子中,layer 并不会显著影响性能,但如果 layer 透明,或者有圆角、变形等效果,就会影响绘制速度了。解决办法可以使用预渲染图像。
- (void)drawRect:(CGRect)rect { if (image) { [image drawAtPoint:imagePoint]; self.image = nil; } else { [placeHolder drawAtPoint:imagePoint]; } [text drawInRect:textRect withFont:font lineBreakModel:UILineBreakModelTailTruncation]; }
- 不要做多余的绘制工作。 在实现 drawRect:的时候,它对 rect 参数就是需要绘制的区域,这个区域以外的不需要进行绘制。
- 预渲染图像。你会发现即使做到了上述几点,当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在图形上下文中画,导出 UIimage 对象,然后再绘制到屏幕。(头像圆角,或者其他变形的时候,用图形上下文能提高性能。)异步绘制。
- 不要阻塞主线程。tableView 在更新数据的时候,整个界面卡主不动,完全不响应应用的用户请求。常见的是网络请求,等待时间长。解决方案:使用多线程,让子线程去执行这些函数或方法。
- 注意:当下载线程数超过2时,会显著影响主线程的性能所以在不需要响应用户请求的时,下载线程数可以增加到5,不建议再加了,以加快下载速度。如果用户正在交互,应把线程数量控制在2个以内。
- 提前计算并缓存好高度。因为 heightForRowAtIndexPath 调用非常频繁
- 选中正确的数据结构:学会选中对业务场景最适合的数组结构是写出高效代码的基础。如果,数组:有序的一组值。使用索引来查询比较快,使用值查询很慢,插入、删除很慢。字典:存储键值对,用键查找比较快。集合:无序的一组值,用值来查询很快,插入、删除很快。
- gzip、zip 压缩:当从服务器下载相关附件时,可以通过 gzip、zip 压缩后再下载,使得内存更小,下载速度更快。
tableview 的 cell 如何嵌套 collectionView?
- 思路用网易新闻类似,用自定义的继承 UITableViewCell 的类,在 initWithFrame 的构造方法中,初始化自定义的继承自 UICollectionView
下拉和上拉的原理
- 以 tableView 的上拉刷新为例:
- 为了进行无缝阅读,通过 tableView 的代理方法,willDisplayCell 判断是否是最后一行
- 如果是最后一行,在显示最后一行的同时,判断当前是否存在上拉刷新
- 如果没有上拉刷新,就进行加载数据,启动小菊花
- 以 tableView 下拉刷新为例
- 判断当前的上拉刷新视图是否有动画
- 如果没有动画,就不是上拉刷新
- 然后下拉刷新加载数据
- 加载完毕数据关闭数据
如何实现 cell 的动态行高?
- 如果希望每条数据显示自身的高,必须设置两个属性,1.预估行高,2.自定义行高
- 设置预估行高 _tableView.estimatedRowHeight = 200;
- 设置自定义行高 _tableView.rowHeight = UITableViewAutomaticDimension;
- 如果让自定义行高有效,必须让容器视图有一个自下而上的约束
谈谈 webview
- iOS 开发中 webview 和 native code 的配合使用的一些经验和技巧。
- webview 与运维成本低,更新几乎不依赖 App 的版本,但在交互和性能上与 native code 有很大的差距。
- native code 与之对应。
- h5确实给 web 带入了一个新时代。这个时代是什么?web App。也就是说,只有脱离 native 的这个前提,在浏览器的环境下,h5的意义才鞥呢显现
awakeFormNib 与 viewdidload 区别
- awakeFromNib:当.nib 文件被加载的时候,会发送一个 awakeFromNib 消息到 nib 文件中的没个对象,每个对象都可以定义自己的awakeFromNib函数来响应这个消息,执行必要操作。也就是说 nib 文件创建 View 对象执行awakeFromNib
- viewdiidload:当 View 对象被加载到内存就会执行 Viewdidload,不管是通过 nib 还是代码形式,创建对象就会执行 viewdidload
layoutSubview 何时调用?
- 初始化 init 方法时不会触发
- 滚动 UIScrollView 触发
- 旋转屏幕触发
- 改变 View 的值触发,前提是 frame 改变了
- 改变 UIView 的大小触发
viewcontroller 的 didReceiveMemoryWarning在什么时候调用,默认操作是什么?
- 引用程序收到来自系统的内存警告的时候,调用didReceiveMemoryWarning方法
- 默认做法:控制器上的 View 不再窗口上显示时,调用 ViewWillUnload,直接销毁View,并调用ViewDidUnload
如何实现瀑漂流,流水布局
- 使用 UICollectionView
- 使用自定义的 FlowLayout
- 需要在 layoutAttributeForElementsInRect 中设置自定布局的 item 的 frame
- 在 prepareLayout 中计算布局
- 遍历数据内容,根据索引取出对应的 attributes(使用 LayoutAttributesForCellWithIndexPath),根据九宫格算法设置布局
- 细节1:实际布局,重写shouldInvalidateLayoutForBoundsChange(bounds 改变从新布局,scrollView 的 contextOffset>bounds)
- 细节2:计算设置 itemSize(保证内容显示完整,UICollectionView 的 context size 是根据 itemsize 计算的)根据列最大高度、对应列数量求出,最大高度累加得到
- 细节3:追加 item 到最短列,避免底部参数不齐
实现突破轮播图
- scrollView 只需要设置三个 imageView 即可,并且默认显示中间的 imageView
- 根据 scrollView 的移动情况,迅速变化三个 imageView 中的图片数据
- imageView 更新完毕后,偷偷把 scrollView 拉回到中间的imageView 位置,这样视觉效果上就实现了无限循环的效果。
load initialize 方法的区别
- (void)load;
- 当类对象被加入项目时,runtime 会向每个类发送 load 消息
- load 方法绘制每个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类,子类优先于分类。
- load 方法不会被类自动继承
- (void)load;
- (void)initialize;
- 也是在第一次使用这个类的时候会调用这个方法。
- (void)initialize;
如何播放 gif 图片,有什么优化方案??
- UIImageView 用来显示图片,使用 UIImageView 中的动画数组来实现动画效果
- 用 UIWebView 来显示动态图片
- 第三方框架
事件传递与响应的过程?
- 在产生一个事件时,系统会将该事件加入到一个由 UIApplication 管理的事件队列里去,UIApplication 会从事件队列中取出最前面的事件,将它传递给先发送事件给应用程序的主窗口。
- 主窗口会调用 hitTest 方法寻找最适合的视图控件,找到后会调用控件的 touches 方法来做具体的事情。
- 当调用 touches 方法,它的默认做法,就会将事件顺着响应者链条上往上传递,传递给上一个响应者,接着就会调用上一个响应者的 touches 方法。
有哪些常见的 crash 场景?
- 访问了僵尸对象
- 访问了不存在的方法
- 数组越界
- 在定时器下一次回调前将定时器释放掉
LLDB(GDB)常用的调试命令?
- p 输出基本类型
- po 输出 oc 对象
- expr 可以在调试中执行表达式,并打印结果
如果一个函数10中有7次正确,3次错误,问题可能出现在什么地方?
- 从函数的所有条件分支类分析可能的错误。
- 检测函数参数是否存在异常。
如何对 iOS 设备进行性能测试?
- profile->instruments->Time Profile 进行性能测试
- App 使用过程中,接听电话。
- App 使用过程中,收到推送。
- 设备充电时,App 的影响以及流畅度。
- 设备在不同电量(低于10%、50%、95%)时,App 的响应以及流畅度
- 意外断电,App 数据丢失情况。
- 网络变化时,App 是如何响应的。
- 多点触摸的情况。
- 跟其他 App 之间进行交互时的响应。
- 杀死进程在重新打开的反馈
- iOS 系统语言环境的变化
AFNetworking 断点续传
- 断点续传的主要思路:
- 检查服务器的文件信息
- 检查本地文件
- 如果比服务器小,断点续传,利用 HTTP 请求头的 Range实现断点续传
- 如果比服务器大,重新下载
- 如何一样,下载完成
简单描述一下客户端的缓存机制?
- 缓存可以分为:内存数据缓存、数据库缓存、文件缓存
- 每次想获取数据的时候
- 先检测内存中有无缓存数据
- 在检测本地有无缓存数据(数据库、文件)
- 最终发送网络请求
- 将服务器返回的网络数据进行缓存(内存、数据库、文件)
什么时候序列化和反序列化,用来做什么
- 序列化把对象转化为字节序列的过程
- 反序列化把直接序列恢复成对象
- 把文件写到文件或者数据库中,并读取出来
iOS 中常用的数据存储方式有哪些?
- 数据存储方法:UserDefault、KeyChain、File、DB
- file:Plist、Archiver、Stream
- DB:Core Data、FFDB