知识点比较杂,希望大家能见谅,如有不足,欢迎指正!如果这篇文章刚好对您有所帮助,请不要吝啬您的喜欢或关注本人,谢谢您的支持!
1. frame 和 bounds 的区别
- frame 表示的是控件矩形框在父控件中的位置和尺寸,是以父控件的左上角为坐标原点
- bounds 表示的是控件矩形框的位置和尺寸,是以自己的左上角为坐标原点
2. 引用资源的时候每个选项的含义
- copy:勾选copy,会把资源拷贝一份到项目的文件夹中(建议勾选,因为这样修改项目中的资源不会影响源资源)
- Added folder:
- 如果勾选 Create groups,只会会创建一个虚拟的文件夹,程序打包后,安装包中不存在这个文件夹
- 如果勾选 Create folder references,真的创建一个文件夹,程序打包后,安装包中真的有这个文件夹
- Add to targets:要不要把资源打包到软件安装包中去,一定要勾选,不勾选到时候程序打包后,安装包中没有这个资源
3. 修改控件 frame 属性的方式
- 直接使用 CGRectMake 函数
- 利用临时结构体变量
- 直接运用结构体赋值
4. 通过 imageNamed:这个方法加载图片有什么特点
- 有缓存
UIImage *image =[UIImage imageNamed:@"图片名"];
- 使用场合:图片比较小、使用频率比较高
- 建议:把需要缓存的图片放到 Images.xcassets
5. @property 的使用策略
- assign
- 基本数据类型、枚举、结构体等非 OC 对象
- weak
- OC对象类型(比如 NSArray、NSDate、NSNumber、模型类)
- strong
- OC对象类型(比如 NSArray、NSDate、NSNumber、模型类)
- copy
- 只适用于 NSString / Block
- 一个对象只要有强指针引用,就不会被销毁
- 当 2 个对象相互引用, 一端用 strong, 一端用 weak
6. 用一个属性引用 UI 控件的时候为什么可以用 weak
- 因为 UI 控件添加到父控件中以后,会有强指针指向这个对象,就已经可以保证这个对象不会被销毁,用弱引用就可以
7. 如果是通过 xib 或者 storyboard 创建控件,初始化的操作可以在 initWithFrame:方法中做吗
- 如果是通过 xib 或者 storyboard 创建控件,初始化时是不会调用 initWithFrame
- 会调用 initWithCoder,初始化完毕会调用 awakeFromNib方法,建议在 awakeFromNib 中做初始化
- 同样的,通过 alloc / init 或者 alloc / initWithFrame 创建控件不会主动加载 xib,即使 xib 的名称和控件的类名一样也不会加载
8. 什么是适配
- 适应、兼容各种不同的情况
- 系统适配:针对不同版本的操作系统进行适配
- 屏幕适配:针对不同大小的屏幕尺寸进行适配
9. 通过代码添加约束的原则
- 对于两个同层级 view 之间的约束关系,添加到它们的父 view 上
- 对于两个不同层级 view 之间的约束关系,添加到他们最近的共同父 view 上
- 对于有层次关系的两个 view 之间的约束关系,添加到层次较高的父 view 上
10. 设计框架需要考虑的问题
- 侵入性:侵入性大就意味着很难离开这个框架
- 易用性:比如少量代码实现 N 多功能
- 扩展性:很容易给这个框架增加新功能
11. 监听某些事件的方法
- 通知( NSNotificationCenter \ NSNotification )
- 任何对象之间都可以传递消息
- 使用范围
- 1个对象可以发通知给多个对象
- 1个对象可以接受多个对象发出的通知
- 要求:必须得保证通知的名字在发出和监听时是一致的
- KVO
- 仅仅是能监听对象属性的改变(灵活度不如通知和代理)
- 代理
- 使用范围
- 1个对象只能设置一个代理(假设这个对象只有1个代理属性)
- 1个对象能成为多个对象的代理
- 使用范围
- 如何选择 ?
- 代理比通知规范
- 建议使用代理多于通知,能使用代理尽量使用代理(通知相对消耗性能)
- 上下级之间一般使用代理
- 跨级之间一般使用通知
12. 程序的完整启动流程
- 执行 Main
- 执行 UIApplicationMain 函数
- UIApplicationMain 的内部会创建 UIApplication 对象,并设置它的代理
- 创建一个事件循环,开启一个主运行循环(死循环,保证程序可以一直运行)
- 加载配置文件 info.plist 读取配置信息
- 判断 info.plist 文件当中有没有 Main storyboard file base name,里面有没有指定 Main.storyboard 文件
- 如果指定,就会去加载 Main.storyBoard 显示出来
- 如果没有, 就什么都不去做
- 通知应用的代理 AppDelegate,应用程序启动完毕,最后调用 application : didFinishLaunchingWithOptions
13. UIWindow 是什么
- UIWindow 是一种特殊的 UIView,通常在一个 app 中至少有一个 UIWindow
- iOS 程序启动完毕后,创建的第一个视图控件就是 UIWindow,接着创建控制器的 view
- 最后将控制器的 view 添加到 UIWindow 上,于是控制器的 view 就显示在屏幕上了
- 一个 iOS 程序之所以能显示到屏幕上,完全是因为它有 UIWindow
14. LoadView 作用以及使用 LoadView 的注意点
- 控制器调用 loadView 方法创建控制器的 view
- 它的默认做法是:
- 先去判断当前控制器是不是从 StoryBoard 当中加载的,如果是,那么它就会从 StoryBoard 当中加载控制器的 View
- 如果不是从 StoryBoard 当中加载的, 那么它还会判断是不是从 Xib 当中创建的控制器
- 如果是,那么它就会从 xib 加载控制器的 View
- 如果也不是从 Xib 加载的控制器,那么它就会创建一个空的 UIView 设为当前控制器的 View
- 注意点:
- 一旦重写了 loadView,表示需要自己创建控制器的 View
- 如果控制器的 View 还没有赋值,就不能调用控制器 View 的 getter 方法,不然会造成死循环
- 因为控制器 View 的 getter 方法底层会调用 loadView 方法
15. KVC 底层实现
- 拿字符串与当前类的属性进行匹配,如果匹配到,就给该属性赋值
- 首先找有没有跟 key 值相同名称的 setter 方法,如果有,就会调用 setter 方法,把 obj 传入
- 如果说没有 setter 方法,那么它会去找有没有相同名称,并且带有下划线的成员属性,如果有就会给该属性赋值
- 如果也没有带有下划线的成员属性,就会找有没有跟它相同名称的成员属性,如果有就会给该属性赋值
- 如果还没有跟它相同名称的成员属性,就会调用 setValue:(id)value forUndefinedKey:
- 如果没有实现 setValue:forUndefinedKey: 就直接报错
16. 导航控制器 View 的结构
- 导航控制器 View 的结构为一个专门存放栈顶控制器 View 的 View
以及一个导航条,导航条的高度为 44,Y 值为 20
17. 导航 push 做了哪些事情
- 当调用 push 方法时, 会把要 push 的控制器添加到导航控制器管理的栈中
- 把之前导航控制器中栈顶控制器 View 给移除,把当前栈顶控制器添加上去
18. 导航 pop 做了哪些事情
- 当调用 pop 方法时,会把要 pop 的控制器从栈里移除
- 把之前导航控制器中栈顶控制器 View 移除,把当前栈顶控制器添加上去
19. 控制器 View 的生命周期
- loadView:加载 view 的时候调用
- viewDidLoad:当控制器 View 加载完毕时调用
- viewWillAppear:当控制器 View 即将显示时调用
- viewWillLayoutSubviews:当控制器 View 即将布局子控件时调用
- viewDidLayoutSubviews:当控制器 View 布局子控件完毕时调用
- viewDidAppear:当控制器 View 显示完毕时调用
- viewWillDisappear:当控制器 View 既将消失时调用
- viewDidDisappear:当控制器 View 消失完毕时调用
- viewDidUnload:当控制器 View 卸载的时候调用
20. 代理传递设计
- 定义协议,在协议方法中,把要传的数据,写成参数
- 定义代理属性
- 调用代理方法
- 设置代理
- 遵守协议
- 实现协议方法
21. iOS 中的存储方式
- Plist 文件存储
- 偏好设置
- 归档
- SQLLite 存储
- CoreData 存储
22. 沙盒目录结构
- Documents:保存应用运行时生成的需要持久化的数据,iTunes 同步设备时会备份该目录
- tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除,应用没有运行时,系统也可能会清除该目录下的文件
- Library / Caches:保存应用运行时生成的需要持久化的数据,iTunes 同步设备时不会备份该目录
- Library / Preference:保存应用的所有偏好设置,iOS 的 Settings (设置) 应用会在该目录中查找应用的设置信息
23. NSUserDefaults 的实现是什么存储,一般用来存什么
- NSUserDefaults 不需要考虑文件名称跟文件的路径
- NSUserDefaults 本质就是 plist 存储 ( NSDictionary )
- 在NSUserDefaults 当中,key 值不能为 nil
- 在NSUserDefaults 不能存放自定义的对象
- 一般用来存用户的一些偏好,如版本号,密码,帐号等
24. initWithCoder 什么时候调用,与 awakeFromNib 的区别
- initWithCoder:当开始解析一个文件时调用
- 如果一个 View 是从 xib 当中加载,当开始解析 xib 时会调用,这个方法
- 在调用此方法时,里面的子控件都还没有创建
- awakeFromNib:当前对象从 nib 文件当中加载完结时调用
- awakeFromNib调用时,说明 xib 加载完结了,View 的大小和里面的子控件都已经确定
25. 使用导航控制器有哪些注意点
- 凡是在导航控制器下的 scrollView 都会自动设置一个内边距 64
- 可以使用 automaticallyAdjustsScrollViewInsets 属性取消内边距
- 给导航条以及导航条里面的控件设置透明度是没有效果的
- 如果说不设置背景图片,会默认给设置一张透明的图片
- 只要是想要设置背景图片,必须得要使用 UIBarMetricsDefault 样式
- 同一个导航控制器下, 导航条只有一个
26. 事件传递响应的过程
- 当发生一个触摸事件后,系统会将该事件加入到一个由 UIApplication 管理的事件队列中
- UIApplication 会从事件队列中取出最前面的事件,交给主窗口
- 主窗口会调用 hitTest 方法寻找最适合的视图控件来处理触摸事件
- 找到最合适的视图控件后,就会调用控件的 touches 方法来作具体的事件处理
- 这些 touches 方法的默认做法是:将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理,接着就会调用上一个响应者的 touches 方法
27. 一个控件不能接收事件的原因
-
不接收用户交互时不能够处理事件
userInteractionEnabled = NO;
注意:UIImageView的userInteractionEnabled 默认就是 NO
-
当一个控件隐藏的时候不能够接收事件
Hidden = YES
-
当一个控件为透明的时候不能接收事件
alpha <= 0.01;
父控件不能接收事件,那么子控件也不能接收事件
子控件超出父控件的大小
28. 如何寻找上一个响应者
- 如果当前的 View 是控制器的 View,那么控制器就是上一个响应者
- 如果当前的 View 不是控制器的 View,那么它的父控件就是上一个响应者
- 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给 UIWindow 对象进行处理
- 如果 UIWindow 对象也不处理,则其将事件或消息传递给 UIApplication 对象
- 如果 UIApplication 也不能处理该事件或消息,则将其丢弃
29. tableView 的性能优化
- tableView 的缓存机制(重用 cell)
- 在不等高 cell 当中,提前计算 cell 的行高,提前估一个行高(200 ~ 250)
- 如果 cell 当中有圆形图片,图片不要用 ImageView 加载 layer.corneadius 裁剪去做,会造成离屏渲染,用绘图 Qurarzds 裁剪,生成一张圆形的图片
- 如果图片的宽高指定为小数点会造成锯齿,造成锯齿就会导致离屏渲染
- cell 当中的 ImageView 的大小最好是跟 UIImage 一样大,如果不一样大,会对 UIImage 做形变操作
- 做 tableView 的时候一定要用真机测试
- 如果是从网络加载数据,一定要放到子线程(异步加载)当中做
- 加载完毕的数据一定做本地缓存
- cell 当中不要动态添加子控件,一般在创建时就把要出现的 cell 给添加进去,暂时不显示的,可以隐藏
- 尽量减少 cell 内部子控件的个数
- 如果控件非常多,把不需要与用户进行交互的控件,通过异步绘制画出来,生成一张图片,把图片添加到 cell 当中
30. 多线程技术的优劣
- 优点:
- 能适当提高程序的执行效率
- 能适当提高资源利用率(CPU、内存利用率)
- 缺点:
- 创建线程需要时间开销:大约需要90毫秒的创建时间
- 创建线程需要空间开销:iOS 下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用 -setStackSize: 设置,但必须是4K的倍数,而且最小是16K)
- 如果开启大量的线程,反而会降低程序的性能
- 线程越多,CPU 在调度线程上的开销就越大
- 程序设计更加复杂:比如线程之间的通信、多线程的数据共享
31. 线程锁
- 解决多线程访问同一块资源造成的线程安全的问题
- 如何加互斥锁:
- @synchronized (锁对象) { // 需要锁定的代码 }
- 加锁的注意点:
- 加锁需要消耗大量的CPU资源
- 注意加锁的位置
- 注意加锁的前提条件
- 注意加锁的锁对象,锁定1份代码只用1把锁,用多把锁是无效的
32. 简单介绍 GCD 技术
- GCD中有两种类型的队列,一种是并发队列,一种是串行队列
- 并发队列在调度任务的时候,是把前一个任务取出来之后就继续取后面的任务,所以如果并发队列结合能够开线程的函数来使用的话,内部会开线程并发的执行任务
- 串行队列在调度任务的时候,是把前一个任务取出来等该任务执行完毕之后再执行后面的任务
- GCD 中的并发队列有两种获得方式,一种是直接使用 dispatch _ queue _ create 函数创建(DISPATCH _ QUEUE_CONCURRENT),一种是直接使用全局并发队列(dispatch _ get _ global _ queue(0,0))
- GCD中的串行队列有两种获得方式,一种是直接使用 dispatch _ queue _ create 函数创建(DISPATCH _ QUEUE _ SERIAL),一种是主队列
- 主队列是一种特殊的串行队列,凡是被添加到主队列中的任务都将在主线程中执行
33. 简单比较 GCD 中的全局并发队列和使用dispatch _ queue _ create函数创建的并发队列异同
- 全局并发队列在整个应用程序中本身是默认存在的并且对应有高优先级、默认优先级、低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用,而 Create 函数是实打实的从头开始去创建一个队列
- 在 iOS6.0 之前,在 GCD 中凡是使用了带 Create 和 retain 的函数在最后都需要做一次 release 操作,而主队列和全局并发队列不需要我们手动 release
- 在 iOS6.0 之后 GCD 已经被纳入到了 ARC 的内存管理范畴中,即便是使用 retain 或者 create 函数创建的对象也不再需要开发人员手动释放
- 在使用栅栏函数的时候,栅栏函数只有在和使用 create 函数自己的创建的并发队列一起使用的时候才有效
34. 对图片进行二级缓存的实现思路
- 先检查该图片对应的内存缓存
- 如果存在内存缓存,则直接使用设置并显示图片
- 如果内存缓存中没有则继续检查该图片对应的磁盘缓存是否存在,跳转到第 2 步。
- 检查该图片对应的磁盘缓存
- 如果存在磁盘缓存,则先保存一份到内存缓存中(方便下次使用),然后设置并显示图片
- 如果不存在磁盘缓存,则直接下载该图片,下载完成后
- 保存一份到内存缓存中
- 保存一份到磁盘缓存中
- 设置并显示图片
35. 简单对比 GCD 和 NSOperation 两种多线程的实现方案
- GCD 是纯 C 语言的 API,而操作队列则是 Objective-C 的对象
- 在 GCD 中,任务用块(block)来表示,而块是个轻量级的数据结构;相反操作队列中的『操作』NSOperation 则是个更加重量级的 Objective-C 对象
- 具体该使用 GCD 还是使用 NSOperation 需要看具体的情况,如果只是想简单开一个子线程执行任务推荐使用 GCD,如果有很多任务需要开多个子线程
- 下载推荐使用操作队列
36. 简单介绍操作队列
- 操作队列本身是 OC 语言的,在 iOS 开发中可以用来实现多线程编程
- 操作队列有两大核心的概念,一个是操作(NSOperation),一个是队列(NSOperationQueue),操作用来封装任务,队列用来存放操作
- 要使用操作队列进行多线程编程,只需要把封装好的操作提交到相应的队列中即可,系统内部会视情况自动开启相应的线程来执行任务
- 在操作队列这门技术中,系统提供了两个子类可以来封装任务,一个是 NSInvocationOperation,一个是 NSBlockOperation,除此之外也可以直接自定义操作
- 操作队列中有两种队列,一种是通过 [NSOperationQueue mainQueue] 获得的主队列,一种是通过 [[NSOperationQueue alloc]init] 方法获得的非主队列
- 主队列是和主线程相关的串行队列,提交到主队列中的操作将被安排在主线程中执行(可以利用该特性来处理线程间通信的相关逻辑)
37. NSRunloop 和线程的关系
- 线程和 runloop 是一一对应的关系(字典)
- 主线程对应的 runloop 是默认创建并启动的
- 子线程对应的 runloop 需要手动的创建并启动
- 线程销毁后 runloop 也要销毁
38. SDWebImage 框架
- SDWebImage 框架是一款非常流行的用来处理图片下载和缓存的第三方框架
- SDWebImage 框架为我们提供了高性能异步下载图片的方案,内部使用 GCD 等多线程相关技术
- 使用 SDWebImage 框架来下载图片,内部默认会对图片进行内存缓存和磁盘缓存的二级缓存结构
- 该框架为 UIButton,UIImageView 等 UI 控件提供了分类,能够方便的处理相关控件图片的远程下载和缓存设置
- 该框架内部还提供了 GIF 图片播放,判断图片类型等一般功能
- 内部使用 NSCache 来专门处理内存缓存
- 在进行清理缓存时:
- clearDisk:直接把整个缓存文件删除,删除之后创建一个新的空文件
- cleanDisk:先删除过期的缓存文件,然后计算当前剩余缓存文件的大小
- 如果该数值超过设定的最大缓存大小,那么就按照文件创建的时间从远到近依次删除,直到整个剩余缓存文件大小小于设定的最大缓存大小为止
- 框架主要结构:
- 管理者(SDWebImageManager)
- 缓存处理组件(SDImageCache)
- 下载处理组件(SDWebImageDownloader | SDWebImageDownloadOperation)
- 分类
39. HTTP 通信的过程
- 请求:
- 如果客户端想要获得相应的数据,那么就对着服务器发送一个请求,请求是客户端向服务器索要数据的过程
- 响应:
- 服务器接收到客户端的请求之后,需要对该请求作出反应,响应是服务器端把数据返回给客户端的过程
- 请求分为两部分,一个是请求头,一个是请求体(GET请求没有请求体)
- 请求头是对客户端信息和请求本身的描述
- 请求体存放要发送给服务器端的具体数据
- 响应分为两部分,一个是响应头,一个是响应体
- 响应头是对服务器端信息和响应数据本身的描述
- 响应体存放要发送给客户端的具体数据
40. JSON 和 XML
- JSON 和 XML 都是一种用来表示数据的一种数据格式,JSON 更加轻量级
- 服务器返回的数据通常是 JSON 或者是 XML 两种
- JSON 数据格式和 OC 对象中字典和数组有些相似
- XML 又称为 XML 文档,XML 的语法结构由三部分构成分别是文档声明,元素和属性
- 如果服务器返回的数据是 JSON,那么在开发中通常需要对 JSON 数据进行反序列化处理,把 JSON 数据转换为 OC 对象
- 如果服务器返回的数据是 XML 格式的,那么需要对 XML 文档进行解析,解析 XML 的方式有两种
- SAX(从根元素开始解析)
- DOM(先把整个 XML 文档加载进内存再解析)
41. 网络安全的原则
- 在网络上不允许传输用户隐私数据的明文
- 在本地不允许保存用户隐私数据的明文
由于篇幅有限,难免知识概括不全,更多知识请前往我的个人文集进行查阅
多线程与网络:http://www.jianshu.com/notebooks/7152576/latest
------------------------ 我是一条调皮的分割线 ------------------------