2、viewController的生命周期
alloc 创建对象分配空间
Init 初始化对象、初始化数据
Loadview 从nib中加载view
ViewDidload 视图加载完成, 可以初始化其他控件
ViewWillappear 视图即将显示
viewDidappear 视图已经显示
ViewWillDispAppear 视图即将消失
VIewDidDispear 视图已经消失
Dealloc 视图已经被销毁
3、Uiview、calayer的区别
UIview与Calayer 的关系
每一个UIview内部默认都会有一个layer层 view负责创建并管理这个layer 实际上uiview之所以能够显示 就是因为内部有这个layer层,view只是对layer的一层封装 并实现了layer的一些代理方法。其实layer是view内部的具体实现
UIview与Calayer 的区别
1、UIview继承UIResponder 具有响应事件的能力,layer继承NSObject 不具备响应事件的能力这也是两者最大的区别
2、一个layer的fream 是由它的anchorpoint、position、bounds和transform共同决定的,而一个view的fream 仅仅只是返回了它layer的fream
3、uiview侧重内容的管理和显示 layer侧重内容的渲染
4、在做iOS动画的时候 修改非Rootlayer的属性(如:位置、背景色)等都会有默认的隐式动画 而View没有
4、UIResponder
事件的传递与响应
事件的传递也就是寻找事件的第一响应者 是由父控件传递给子控件
顺序是:硬件--》系统--》UIapplication---》uiwindow--》superView ---》subView
确定了事件的第一响应者就确定了事件的响应链
1、首先由最上层的view来尝试处理事件、如果处理不了则传递给父视图superview
2、superview如果处理不了则传递给他的父视图viewcontroller.view
3、viewcontroller.view 如果处理不了则传递给viewcontroller
4、viewcontroller处理不了则传递给 UIWindow
5、UIWindow 如果处理不了则传递给UIapplecation
6、UIapplecation 如果处理不了 则事件丢弃
5、weak的相关知识 它与assign有什么区别
Weak 是弱引用 使用weak修饰的对象 其引用计数并不会增加,而且当对象被释放的时候weak会自动将指针置为nil,同时weak也可以解决循环引用问题
weak的原理是
runtime维护了一张weak表用于存放对象的weak指针地址数组,weak表也是哈希表,weak指针指向的对象地址作为key值 weak指针地址数组作为value
初始化的时候runtime会调用initweak函数来创建一个weak指针地址
添加引用的时候 runtime会调用storeweak函数来跟新这个weak指针地址 然后创建一张weak表
移除的时候 runtime会调用clearDeallocing函数 然后根据对象的地址 找到指向对象的weak指针地址数据 然后遍历数组将数据置为nil 最后从weak表中删除这个记录
Assgin 修饰基本数据类型 也可以修饰对象类型 当assgin修饰对象类型的时候 一旦这个对象被释放 assgin修饰的对象指针并不会将数据置为nil 当再次访问这个指针的时候 就会产生奔溃显像
6、浅copy与深copy
浅拷贝拷贝的是对象的指针,拷贝的指针与原来的指针指向同一块内存
深拷贝 不仅拷贝对象的指针 同时也拷贝了对象 经过深拷贝后会产生一个全新的对象,深拷贝的指针与原来的指针指向不同的地方
copy方法:用于非可变对象 都是浅拷贝 用于可变对象则是深拷贝
mutablecopy方法:无论用于可变对象还是不可变对象 都是深拷贝
7、修饰一个字符串时使用copy 还是strong
平时开发工程中 我是比较喜欢使用copy来修饰字符串
用copy修饰的NSString如果无意被一个NSMutableString类型的变量赋值时 原来的NSString 会被copy出来一份用来存放 NSMutable的值 万一NSMutableString发生改变 并不会影响到NSString的值 这样能体现出 NSString作为不可变类型的性质
用Strong修饰的NSString如果无意被一个NSMutableString类型的变量赋值时 NSString并不会被拷贝 一旦NSMutableString类型的变量值发生改变 NSString的值也将会随之改变。
当然 如果一个NSString 被另一个NSString赋值时 无论使用copy还是strong 效果都一样 都是直接赋值
8、谈谈你对KVO的理解
KVO俗称键值观察者,利用KVO可以监听对象某一属性值的变化
当对一个对象进行KVO时 runtime会动态生成一个子类 这个子类继承原来的类,同时将原来类的isa指针指向这个子类
KVO的本质就是监听属性的setter方法,只要被观察对象有成员变量且实现类setter方法,就会调用foundation框架的willchangeValueforkey方法 然后再调用父类的setter方法 最后调用didchangeValueforkey方法 从而触发KVO的代理方法
当被观察的对象移除所有的观察者时 runtime会自动将isa指向原来的类
9、谈谈你对KVC的理解
KVC俗称键值编码
KVC常见的API有四个
其中赋值的两个分别为
Setvalueforkey
Setvalueforkeypath
取值的也有两个 分别为
Valueforkey
Valueforkeypath
forkey和forkeypath的区别在于 forkeypath可以根据路径来赋值或取值
赋值原理
首先会取查找set方法也就是 setKey 如果找到则直接赋值 如果找不到 则去查找 _setkey 如果找到则直接赋值 如果找不到则去查看 accessInstanceVariableDirctly方法的返回值 如果返回NO 则表示不允许访问成员变量 然后调用setvalueforUndefinedkey方法最后抛出异常
如果返回YES 则去查找
Key _key iskey _isKey 如果找到 直接赋值 如果找不到则调用 setvalueforundefindkey然后抛出异常
取值原理
首先会取找getkey key iskey _iskey 如果找到则直接取值 如果找不到则查看accessInstanceVariableDirectly方法 如果返回NO 则调用valueforundefinedkey然后抛出异常
如果返回YES 则去查找 _key key _iskey iskey如果找到直接取值 如果找不到则调用valueforundefinedkey 然后抛出异常
10、通过KVC修改属性的值会触发KVO吗
会
如果属性实现了setter方法就可以自动触发KVO
如果属性没有实现setter方法 KVC就会自动调用willchangeValueforkey 和 didchangevalueforkey方法来触发KVO
11、谈谈你对runloop的理解
runloop顾名思义就是运行循环 他就相当于一个do-while循环
他的基本作用
1、保证程序持续运行
2、处理app的各种事件 如触摸事件 Timer等
3、节省CPU资源 让程序该做事的时候做事 该休眠的时候休眠
4、。。。
runloop的获取方式有两种
一种是OC方式
获取当前的runloop 【NSRunloop currentRunloop】
获取主线程的runloop【NSRunloop mainRunloop】
一种是C语言的方式
获取当前的runloop CFRunloopgetCurrent()
获取主线程的runloop CFRunloopGetmain()
与runloop相关的5个类
1、CFRunloopRef
2、CFRunloopModeRef
3、CFRunloopTimeRef
4、CFRunloopSourceRef
5、CFRunloopObserverRef
一个runloop包含若干个mode 每一个mode中又包含 source0/source1/timer/observer
runloop启动的时候只能选择其中的一个mode 如果想切换mode 就必须退出当前mode 然后再进入另一个mode
如果一个runloop mode里面没有 source0/source1/timer/observer就会立即退出runloop
Runloop中常见的mode有两种 一种是NSDefaultRunloopMode 也就是app默认的mode 通常主线程就是在当前mode下运行
一种是UITrackingMode 界面追踪的mode 用于scrollview触摸滑动页面的追踪 能够保证页面在滑动的时候不受其他mode的影响
12、线程与runloop的关系
1、每条线程都有唯一的一个与之对应的runloop
2、runloop存放在一个全局的字典中 线程作为key runloop作为value
3、主线程的runloop默认情况下是开启的 子线程的runloop默认情况是不被开启的 只有我们第一次去获取他的时候才会被开启
4、runloop在线程结束的时候会自动销毁
13、讲一讲runloop项目中有用到过吗
1、线程保活
2、解决NStimer 在页面滑动时不准确的bug
3、监控卡顿
14、 timer与runloop的关系
一个runloop内部有多个mode 每个mode中包含source0/source1/timer/observer
runloop运行的时候只能选择其中的一个mode 而且runloop的mode中如果没有source0/source1/timer/observe runloop就会立刻退出
15、 程序中添加每3s响应一次NStimer 当拖动tableview时 timer 可能无法响应 要怎么解决?
runloop通常情况下有两种mode 一种是 NSdefaultRunloopMode 一种是UITrackingRunloopMode 程序默认是处于NSdefaultRunloopMode 但是页面滑动就会退出 NSdefaultRunloopMode 进入UITrackingRunloopMode 所以timer会停止 想要解决这个问题 就必须将NSTimer 添加到任何模式下都能工作 苹果提供了一个标记 也就是 NSRunloopCommonModes 也就是 【【NSRunloop currentrunloop】addtimer :timer forMode:NSRunLoopCommonMode】 这样就解决了
16、多线程
iOS的多线程有四种分别为 Pthreads、NSThread、GCD、NSOperation和NSOperationQueue
Pthreads 基于C语言使用难度比较大 一般底层操作线程使用,其生命周期由程序员控制。
NSThread 是苹果提供的一个轻量极的面向对象的一个多线程的解决方法,一个NSthread就是一个对象。其生命周期是由程序员控制。
GCD,基于C语言代码封装成block块可以多核并行运算,简单易用。
GCD有两个核心的概念,任务和队列
任务分为:同步和异步
两者最大的区别就是有没有开辟线程的能力
同步:不具备开辟线程的能力
异步:具备开辟线程的能力
队列
分为串行队列和并发队列
两者最大的区别在于任务能否同时执行
串行队列 任务只能在当前线程执行,任务一个接着一个执行,前一个任务执行完毕后一个任务才能开始执行
并发队列:任务可以在多条线程同时执行,并发队列只有在异步的时候才能起作用
NSOperation和NSoperationQueue 是对GCD更高一程的封装,比GCD更加简单易用,其生命周期由系统控制,
使用NSOperation和NSoperationQueue
可以设置操作的依赖关系,可以设定操作的优先级,
可以使用KVO监听一个操作的状态
17、线程锁
多线程存在的问题就是 多条线程同时访问同一块资源,容易发生数据错乱等安全问题
多线程安全问题的解决方案就是线程同步技术,线程同步技术也就是加锁
iOS提供的锁有很多
OSSpringLock 也就是自旋锁,在iOS10以后使用 os_unfair_lock来代替
os_unfair_lock
Pthread_mutex 互斥锁
Dispatch_semaphore信号量
NSlock
NSrecuriveLock
NScondition
NSconditionlock
等等
OSSpringLock 自旋锁在iOS10 以后被 OS-unfair-lock来代替的原因是 自旋锁不在安全且在加锁解锁的等待过程一直在忙等,浪费cup资源,而且容易出现优先级反转等问题
OS-unfair-lock 在加锁解锁的过程中 线程是处于休眠状态的,
18、怎么设置线程并发数量
使用GCD的dispach_semaphore 也就是信号量来控制线程的最大并发数量
具体代码实现实现
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
NSLog(@"%d---%@",i,[NSThread currentThread]);
sleep(2);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
19、谈谈你谁runtime的理解
OC是一门动态性较强的语言,它允许很多操作延迟到运行时执行
OC的动态性就是由runtime来支持和实现的,利用runtime我们可以做很多的事情
比如
1、利用关联对象给分类添加属性
2、遍历类的成员变量以及方法
3、交换系统方法
4、利用消息转发机制解决方法找不到的问题
20、OC的消息转发机制
OC所有方法调用 到最后都会转化为objc_messageSend
消息转发阶段大致分为三个阶段
第一个阶段:消息发送阶段
首先去查找消息的接受者是否存在 如果不存在 直接退出
如果存在,首先去该接受者类的方法缓存列表中查找方法 如果找到则执行 如果没有找到 则去该类的方法列表中查找该方法 如果找到则执行 然后将该方法缓存到该类的方法缓存列表中,然后执行该方法,如果找不到则去该类的父类的方法缓存列表中查找,如果找到则缓存到该类的方法缓存列表中然后执行,如果找不到则去父类的方法列表中查找,如果找到则缓存到类的方法缓存列表中 然后执行,如果找不到则进入第二个阶段
第二个阶段,方法动态解析阶段
首先会去判断当前方法是否已经解析过 如果解析过则直接走消息发送流程
如果没有解析过 则直接调用 resoveInstanceMethod或则resoveClassMethod方法来动态解析该方法 然后标记已经解析 最后重走消息发送流程
在这个阶段我们可以返回一个方法来代替原来的方法,也就是第一次去补救
如果第二个阶段不做任何处理 则去进入第三个阶段
第三阶段:消息转发阶段
首先会调用 forawardingselector方法,
在此方法中返回一个可以处理该方法的对象
如果该对象存在则直接给对象转发消息
如果该对象不存在则直接调用 方法签名函数
methodSingtrueSelector方法 在此方法中如果返回nil 则直接奔溃
如果返回不为nil 则直接调用forwardInvocation 在这个方法中也可以返回一个能够响应方法的对象
21、https 与http的区别
https需要向机构申请CA证书 极少免费
HTTP 属于明文传输 HTTPS基于SSL进行加密传输
HTTP端口为80 HTTPS端口为443
HTTPS是加密传输 有身份验证环节 更加安全
22、TCP的三次握手
第一次握手:由客户端向服务端发送SYN同步报文
第二次握手:服务端受到客户端发起的SYN包 然后返回给客户端自己的SYN包和ACK确认包
第三次握手:客户端受到服务端返回的ACK确认包 然后建立正式的连接
23、中间人攻击
HTTPS中间人攻击 也就是服务端和客户端中间插入了第三者 客户端和服务端双方通讯的对方变成了中间人 而不是原本的对方
HTTPS 协议之所以安全是因为 HTTPS协议会对传输的内容进行加密处理 而加密的过程即使用了非对称加密又使用了对称加密 非对称加密使用在证书的验证阶段 对称加密使用在数据的传输阶段
证书验证阶段
1、客户端发起https请求
2、服务端受到客户端发送的请求之后返回HTTPS证书
3、客户端验证证书是否合法 如果不合法则弹出警告,如果合法则建立连接
数据传输阶段
1、证书验证合法之后 客户端本地生成随机数
2、然后通过公钥进行加密随机数,并把加密的随机数传输到服务端
3、服务端通过私钥解密客户端发送过来的随机数
4、服务端通过客户端发送过的随机数构造对称加密算法 然后对返回的内容进行加密处理 然后传输
中间人监听的原理就是
1、劫持客户端请求 客户端所有请求均发送到中间人服务器
2、中间人服务器返回自己的证书
3、客户端生成随机数 通过中间人返回证书的公钥 对随机数进行加密 然后上传给中间人服务器
4、此时中间人服务器拥有客户端的随机数可以通过对称加密对内容进行加解密
5、中间人以客户端的身份向服务器发送https请求
6、服务端返回自己的证书
7、中间人 使用服务端返回的证书公钥 加密随机数 发送给服务端
8、服务端利用随机数加密数据然后传输给中间人
9、这样中间人就能获取 通讯双方的传输给容给监听到 这样就构成了中间人监听
24、TCP 与 UDP的区别
TCP:面向连接 传输可靠(能够保证数据的正确性和数据的顺序)、用于传输大量的数据、速度慢、建立连接需要消耗较多资源
25、iOS有哪些设计模式
1、单利模式
单利保证了应用程序的生命周期內仅有一个该类的实例对象,而且易于外界访问。
2、代理模式
委托代理是一种协议 通过@protocol方式实现 代理需要建立代理关系
3、观察者模式
观察者模式定义了一对多的依赖关系,让多个观察者对象同时监听某一主题对象。在iOS中 观察者的具体实现有两种 一种是KVO 一种是通知
26、MVC与MVVM
MVC 也就是model view controller
MVC的特点就是 view上显示什么内容取决于model 只要model的数据发生改变 与其对应的view也会做响应的跟新
但是model和view不能直接通信 需要借助controller来完成
model与控制器之间是通过KVO的方式来间接通讯的 控制器负责初始化model 然后对其进行读写 之后传递给view来显示
MVC有有点也有缺点
优点:实现了业务逻辑与UI逻辑分离 降低了代码的耦合度
缺点:
1、controller 出现臃肿
MVC中view将用户的交互通知给控制器,控制器再通过更新model来反映状态的改变 然后model再通知控制器改变与之对应的view来更新状态,过程复杂
2、MVC中大部分代码都集中在控制器中,使控制器的代码越来越多,控制器不仅要管理自身属性的状态还要遵循许多协议,这样导致协议代码和自身一些逻辑代码混淆在一起不方便管理
3、网络层遗失,没有合适的地方存放网络请求,如果将网络请求放在view/model中,由于网络请求大部分都是异步请求,有可能出现网络请求的生命周期比拥有他的view/model的生命周期更加长,导致网络请求被提前释放
MVVM
Model view viewmodel
MVVM是MVC的一种演进,真正的实现了视图与业务逻辑分离,同时又减轻了控制器的压力
MVVM中正式将view 和 ViewController联系在一起,view 和 viewController 都不能直接引用model 而是引用viewModel
viewModel 是一个存放用户输入及验证逻辑、视图显示逻辑、网络请求的地方,使用MVVM虽然增加了代码量 但是整体来说减少了代码的复杂性。
27、数据安全加密
对称加密 和 非对称加密
对称加密又称公开密钥加密 加密和解密都用同一个密钥 如果密钥被攻击者获取 此时加密就失去了意义 常见的对称加密有 DES AES 等等
非对称加密 使用一对非对称的密钥 一把叫作公钥一把叫作私钥 公钥加密只能用私钥解密 私钥加密只能用公钥解密 常见的非对称加密 RSA
28、如何优化启动时间
app的启动分为冷启动和热启动
一般情况主要是针对app冷启动的优化
app冷启动大致分为三个阶段
第一个阶段:苹果的动态链接器 也就是dyld
在这个阶段 dyld 会动态加载可执行文件以及可执行文件所依赖的库
等到可执行文件以及依赖库加载完毕就会进入第二个阶段 runtime阶段
第二阶段:runtime阶段
在此阶段 runtime会解析可执行文件 然后加载多有类和分类的load方法 然后对一些objc结构进行注册和初始化
第三个阶段 程序入口
app启动优化也是从这三个方面优化的
第一个阶段:删除无用的类以及分类、尽量减少依赖库的使用
第二个阶段:根据实际情况 使用initialize方法代替load方法
第三个阶段:在不影响用户体验的情况下将一些三方库的初始化延迟执行
29、如果优化安装包体积
删除项目中废弃的代码
删除项目中无用的图片
不影响显示效果的情况下尽可能的压缩图片
30、Load 与 initialize的区别
1、加载时机不同
load在app启动的runtime阶段调用当出现继承时 首先会调用父类的load方法 然后是子类的load方法 最后是分类的load方法 而且load方法只会调用一次
,initialize是在类和分类第一次接收到objc_messageSend的时候调用 出现继承的时候 父类的initialize可能会调用多次 但是父类只会初始化一次
2、调用方式不同
load方法是通过指针函数直接调用
Initialize 是通过消息转发机制objec_messageSend的方式调用
31、类和分类
分类并不是真正的类 分类在编译的时候 会生成一个结构体 在运行阶段 会将分类的方法列表、属性列表 等信息添加到类中
32、Block
block的本质其实就是一个OC对象 他到最后也是一个结构题 也有isa指针
block主要有一下注意的点
1、变量捕捉
在开发的过程中我们经常会遇到 block访问外部变量的情况
block访问局部变量的时候 也分情况
1、block访问auto变量的时候 block在创建的时候内部会自动创建一个变量来接受外部变量的值 简称 值捕获
2、block访问static修饰的变量的时候 block在创建的时候内部会生成一个与之对应的指针变量来接受外部的指针指 简称指针传递
总的来说
block内部访问auto修饰的局部变量是 是值传递 原因是 局部变量的作用域在局部 出了局部 这个变量就会被销毁 所以为了能够保证block正常运行 对其进行值捕获
block内部访问static修饰的局部变量时 由于static修饰的局部变量 存放在全局区 一直存在 所以进行的是指针捕获
block内部访问全局变量的时候 什么也不操作 直接访问
2、block的类型
block分为三种类型
NSStaticBlock
NSGlobalBlock
NSMallocBlock
NSStaticBlock 存在栈区
NSGlobalBlock 存放在代码区
NSMallocblock 存放在堆区
只要block 内部没有访问auto变量 就是 NSGlobalBlock
只要block 内部访问了 auto变量 但没有进行 copy操作 就是 NSStaticBlock
即访问了auto变量 又调用了copy方法 则是NSMallocBlock
在ARC环境下 系统会根据实际情况自动将栈区的block拷贝到堆区
如 block被一个强指针引用时
block作为函数的返回参数时
Block 作为GCD API方法参数时
3、block的循环引用
block解决循环引用的方法又三种
第一种 _ _ weak
第二种 _ _ unsafe_unretain
第三种:_ _block
一般情况下使用——weak 因为——weak并不会产生强引用 指向的对象销毁时 weak会将指针自动置为nil
_ _ unsafe_unretain 也不会产生强易用 但是指向的对象销毁时 指针并不会置为nil
4、__block的作用
__block 在平时开发当中 只要是解决 block内部修改局部变量问题
使用__block 修饰的变量 编译器会将其 包装称一个对象 这个对象内部有一个变量来存放外部变量的值 也有一个指针指向自己
33、scoket 相关知识点(概念、三次握手四次断开、心跳包、重连以及重连次数)
Scoket是客户端与服务端相互通信的桥梁,客户端与服务端相互通信就必须建立接连,俗称三次握手
第一次握手:客户端发送SYN请求报文到服务端
第二次握手:服务端接收客户端的SNY请求之后返回自己的SNY包和ACK确认报文
第三次握手:客户端接收到服务端的SNY包和ACK报文之后 就建立了连接
为了保证两端通信时刻保持活跃,客服端会每个一段时间向服务端发送一个心跳包
有时候由于外界原因 比如网络波动等导致两者之间断开链接,我们需要做一个重连机制,而且设置一个最大的重连次数,当超过这个次数时就放弃重连
34、MQTT相关知识点(概念、订阅、发布、心跳包、重连)
MQTT简称消息队列遥测传输协议,他是发布/订阅型消息,由于MQTT具有轻量、简单、开放和易于实现等优点被广泛使用
MQTT有三种身份 即,订阅者、代理、发布者
订阅者和发布者都为客户度啊,代理为服务器 订阅者也可以为发布者
MQTT订阅
MQTT订阅传输的消息 包括主题topic和负载pyload 可以通过负载信息得到是否订阅成功,如果订阅失败 则继续订阅,当订阅次数大于一个固定值时取消订阅
MQTT发布
MQTT发布信息包含主题topic和负载信息,接收者接受到信息之后会根据主题topic来确定执行什么操作
35、推送相关知识点
1、app向iOS设备注册通知,用户需要同意通知
2、iOS设备携带UDID和app的bundle ID 向苹果远程推送服务器注册
3、苹果远程服务器接受到iOS设备发送过来的UDID和bundle ID 向后向推送列表中注册设备 然后返回一个deviceToken给app
4、app接收到deviceToken后把deviceToken上传给自己的服务器由服务器存储
5、当自己的服务器想要给app发送推送消息时,服务器会将消息和devicetoken 一起发送给苹果的远程推送服务器
6、苹果远程推送服务器接受到消息和deviceToken之后 向自己设备注册推送列表中查找设备,然后向设备发送推送消息
7、设备接收到推送消息之后根绝bundle id查找到对应的app app在根据之前设定好的弹出消息通知
36、iOS 存储数据的方式有以下集中
plist文件
NSUserDefaults(沙盒)
文件读写存储
解归档
数据库
Plist
可以存储基本数据类型,常用语存储用户设置或则经常用到且不经常改动的数据,不适合大量数据的存储
NSUserDefaults
用于存储用户的便好设置
文件读写
使用NSFileManger处理 文件存储的路径可以使用代码设置,可以存储大量的数据,但是数据的存储必须一次性操作 所以在频繁操作数据方面性能有所欠缺
解归档
可以存储数据模型,但是解归档必须遵守NSCoping协议,在大批量数据村粗方面性能有所欠缺
数据库
iOS中使用的数据库是sqlite3 在开发过程中我们不经常使用sqlite语句来操作数据库 而是借用第三方工具来对数据进行增删改查,因为sqlite语句太麻烦一不小心就会出错
37、一个app只有一个进程,进程享有app的所有资源
一个进程中可以有多个线程,进程负责资源的调度与分配,线程才是app运行的执行单元 负责代码的执行
38、HTTP协议中 POST 方法和 GET 方法有那些区别?
get方法是向服务器请求数据 post方法是向服务器提交数据
get方法将参数拼接到地址栏 post方法将参数封装到body体里面 所以post请求比get请求安全
get请求url长度有所限制,post请求没有限制
39、如何优化Tableview
1、最常用的就是cell的重用, 注册重用标识符
如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell
如果有很多数据的时候,就会堆积很多cell。
如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell
2、避免cell的重新布局
cell的布局填充等操作 比较耗时,一般创建时就布局好
如可以将cell单独放到一个自定义类,初始化时就布局好
3、提前计算并缓存cell的属性及内容
4、减少cell中控件的数量
5、不要使用ClearColor,无背景色,透明度也不要设置为0因为渲染耗时比较长
6、使用局部更新 如果只是更新某组的话,使用reloadSection进行局部更
7、加载网络数据,下载图片,使用异步加载,并缓存
8、少使用addView 给cell动态添加view
9、按需加载cell,cell滚动很快时,只加载范围内的cell
10、不要实现无用的代理方法,tableView只遵守两个协议
11、不要做多余的绘制工作。在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制
12、使用正确的数据结构来存储数据。
13、少不必要的修改
40、CADisplayLink、NSTimer使用注意(解决CADisplayLink、NSTimer循环引用问题)
1、循环引用问题、CADisplayLink、NSTime 对tager产生强引用,如果target再对定时其产生强引用就会导致循环引用的问题
2、由于CADisplayLink、NSTimer底层都是基于runloop runloop在跑圈的时候由于每次处理的任务消耗的时间有所差异可能会导致定时器不准确
解决循环引用的问题
可以使用代理对象打破循环引用 一般使用NSProxy来解决,如果是NSTimer 也可以使用block解决。
解决定时器不准确的问题
可以使用GCD定时器,直接操作内核
41、isMemberOfClass 与 isKindOfClass 的区别
isMemberOfClass 传入的对象和被比较的对象类型相同才会返回YES
isKindOfClass 传入的对象 与被比较的对象或则被比较对象的子类类型相同才会返回YES
42、子线程是否能够刷新UI
子线程中是可以刷新UI的但是在子线程中刷新UI是不安全的,如果多条子线程同时操作同一UI有可能会出现页面混乱显现。而且如果Xcode开启了主线程监测的话就会有一个蓝色的提示
43、如何检测内存泄露
1、静态检测 command +shift + B
1、逻辑错误:访问空指针或未初始化的变量等;
2、内存管理错误:如内存泄漏等;
3、声明错误:从未使用过的变量;
4、Api调用错误:未包含使用的库和框架。
根据运行出来的结果,一个个去具体分析。这都是工具分析出来了,需要自己甄别是不是可以忽略
2、动态分析 Instruments 是 Xcode 自带的检测调试工具
Leaks:内存检测,内存泄漏检测工具
3、使用三方工具
MLeaksFinder
swift面试题
1、Swift和Objective-C有什么区别?
1)Swift是强类型(静态)语言,有类型推断,Objective-C弱类型(动态)语言
2)Swift面向协议编程,Objective-C面向对象编程
3)Swift注重值类型,Objective-C注重引用类型
4)Swift支持泛型,Objective-C只支持轻量泛型(给集合添加泛型)
5)Swift支持静态派发(效率高)、动态派发(函数表派发、消息派发)方式,Objective-C支持动态派发(消息派发)方式
6)Swift支持函数式编程(高阶函数)
7)Swift的协议不仅可以被类实现,也可以被Struct和Enum实现
8)Swift有元组类型、支持运算符重载
9)Swift支持命名空间
10)Swift支持默认参数
11)Swift比Objective-C代码更简洁
2、讲讲Swift的派发机制
1)函数的派发机制:静态派发(直接派发)、函数表派发、消息派发
2)Swift派发机制总结:
Swift中所有ValueType(值类型:Struct、Enum)使用直接派发;
Swift中协议的Extensions使用直接派发,初始声明函数使用函数表派发;
Swift中Class中Extensions使用直接派发,初始声明函数使用函数表派发,dynamic修饰的函数使用消息派发;
Swift中NSObject的子类用@nonobjc或final修饰的函数使用直接派发,初始声明函数使用函数表派发,dynamic修饰的Extensions使用消息派发;
3)Swift中函数派发查看方式: 可将Swift代码转换为SIL(中间码)
swiftc -emit-silgen -O example.swift
3、Swift如何显示指定派发方式?
添加final关键字的函数使用直接派发
添加static关键字函数使用直接派发
添加dynamic关键字函数使用消息派发
添加@objc关键字的函数使用消息派发
添加@inline关键字的函数会告诉编译器可以使用直接派发
4、Struct和Class的区别?
1)Struct不支持继承,Class支持继承
2)Struct是值类型,Class是引用类型
3)Struct使用let创建不可变,Class使用let创建可变
4)Struct无法修改自身属性值,函数需要添加mutating关键字
5)Struct不需要deinit方法,因为值类型不关系引用计数,Class需要deinit方法
6)Struct初始化方法是基于属性的
5、Swift中的常量和Objective-C中的常量有啥区别?
Objective-C中的常量(const)是编译期决定的,Swift中的常量(let)是运行时确定的
6、?,??的区别
?用来声明可选值,如果变量未初始化则自动初始化nil;在操作可选值时,如果可选值时nil则不响应后续的操作;使用as?进行向下转型操作;
?? 用来判断左侧可选值非空(not nil)时返回左侧值可选值,左侧可选值为空(nil)则返回右侧的值。
7、Swift中mutating的作用
Swift中协议是可以被Struct和Enum实现的,mutating关键字是为了能在被修饰的函数中修改Struct或Enum的变量值,对Class完全透明。
8、Set(集合类型)的使用场景
Set存储值类型相同、无序、去重
9、final关键词的用法
final关键词的作用:它修饰的类、方法、变量是不能被继承或重写的,编译器会报错。另外,通过它可以显示的指定函数的派发机制。
10、lazy关键词的用法
lazy关键词的作用:指定延时加载(懒加载),懒加载存储属性只会在首次使用时才会计算初始值属性。懒加载属性必须声明(var)为变量,因为常量属性(let)初始化之前会有值。
lazy修饰的属性非线程安全的。
11、Swift中的访问控制权限
popen:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上)
public:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写
internal:只允许在定义实体的模块中访问,不允许在其他模块中访问
fileprivate:只允许在定义实体的源文件中访问
private:只允许在定义实体的封闭声明中访问
网友评论