网络问题
HTTP
- get 和 post 的区别
- get通过URL向Server获取数据,也可以在URL里向Server传递数据,传递的数据受限于URL的长度
- post用于向Server传递数据并获得响应,数据通过body发送,数据量比get大
- get参数直接写在URL里,安全性很差
- http2.0的特点
- 二进制分帧在帧首部会预知收到的内容,格式固定。有10帧类型可供使用,解析的数据规范
- 流:一次请求响应构成一个流,frame header里有streamID来表示,通过发送HEADER类型的帧启动一个流
- 流量控制:通过发送WINDOW_UPDATE frame来协商流量
- 优先级:可以为资源建立依赖关系,和优先级,确保资源请求的先后。
- 推送:通过PUSH_PROMISE帧通知客户端,要进行推送
- 首部压缩:重复使用两次请求相同的首部
-
HTTP的POST的body体使用form-urlencoded和multipart/form-data的区别
HTTP 协议是以 ASCII 码传输,HTTP 请求分为三个部分:请求行、请求头、消息主体。 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式,开发者完全可以自己决定消息主体的格式。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码。Content-Type主要有四种:
(1)application/x-www-form-urlencoded:如下图raw body会是name=homeway&key=nokey
,在php中,通过$_POST
就可以获得数组形式的数据
(2)multipart/form-data:
发送数据如下
当文件太长,HTTP无法在一个包之内发送完毕,就需要分割数据。
(3)application/json
(4)text/xml -
HTTPS的加密原理
在TCP和HTTP之间加了一层TLS层确保通信安全。在TCP握手完成之后会进行TLS握手。TLS的目标是为在它之上运行的应用提供三个基本服务:- 加密:通过对称秘钥进行数据加密
- 身份验证:通过证书验证身份
- 数据完整性校验:TLS每条记录都会被签上MAC,通过MAC验证消息的完整性
TLS握手过程,整个过程2RTT
- 协商加密套件服务端提供证书可供客户端验证身份,
- 客户端验证完毕获取证书公钥,客户端生成一对对称秘钥用于加密数据,客户端用公钥把对称秘钥加密后传给服务端。
- 验证消息完整性(通过MAC),服务端获取秘钥。
- 客户端发送加密的“Finished消息”。
如何设计一个网络框架
- URLSession layer:用一个manager来管理session,cachePolicy,securityPolicy,session回调代理
- HTTP Client layer:封装http的各种请求
- Business Layer:业务接口
前两层在AFN已经实现。
iOS的网络缓存策略
TCP/IP
- TCP为什么是3次握手,4次挥手
握手:客户端发送SYN,服务端确认发送ACK,客户端确认ACK+1
挥手:客户端发送FIN,服务端ACK;服务端发送FIN,客服端 - 什么是socket,Socket建立网络连接的步骤?
Socket 是对 TCP/IP 协议的封装,Socket 只是个接口不是协议,通过 Socket 我们才能使用 TCP/IP 协议,除了 TCP,也可以使用 UDP 协议来传递数据。
socket连接步骤:
(1) 服务器监听:服务器并不定位具体客户端的套接字,而是时刻处于监听状态;
(2) 客户端请求:客户端的套接字要描述它要连接的服务器的套接字,提供地址和端口号,然后向服务器套接字提出连接请求;
(3) 连接确认:当服务器套接字收到客户端套接字发来的请求后,就响应客户端套接字的请求,并建立一个新的线程,把服务器端的套接字的描述发给客户端。一旦客户端确认了此描述,就正式建立连接。而服务器套接字继续处于监听状态,继续接收其他客户端套接字的连接请求. - TCP 流量控制
其他
- 项目中你是怎么处理网络速度慢、中断抖动等网络请求中的问题?
- 大文件离线下载怎么处理?会遇到哪些问题?又如何解决
- 用户需要上传和下载一个重要的资料文件,应该如何判断用户本次是否上传成功和下载成功了?
- 如果现在要实现一个下载功能, 你要如何设计。说说每个类具体做什么
- 平时如何实现网络请求, 一般返回的数据是什么格式, 如何解析..
OC Foundation
Block
- 为什么 block 里面还需要写一个 strong self,如果不写会怎么样?
block里面用weak self防止recycle reference,如果block在执行的过程中外部self被释放,那么weak self将被置为nil。如果block执行时weak self已经为nil,那么block操作无效,不会crash;如果block访问weak self时,weak self还没有被置为nil,将会出现野指针问题,会crash。 - weak 的内部实现原理
向__weak标示符标记的变量赋值会调用objc_storeWeak
,这个函数会向weak_table注册一个key-value,key就是对象的地址,value就是weak变量地址数组。 - Block和函数指针的区别?
函数指针指向函数的调用地址,block的结构体构造很像OC对象,在处理block的时候可以像对象那样对block进行内存管理,block结构体里有一个成员指针指向block代码块,这就是一个函数指针。 - 在block里面, 对数组执行添加操作, 这个数组需要声明成 __block吗
对数组执行添加操作并不是改变数组的指针指向,所以数组变量没有被修改,这不需要block。 - 在block里面, 对NSInteger进行修改, 这个NSInteger是否需要声明成__blcok
下面__block变量会被转换成一个结构体,当一个block使用这个变量时,block结构体构造函数会捕获__block变量结构体。从实现中可以看出__forwording
指针指向__block 对象的内存。__forwording
指针的功能是同步__block变量的变化,如果变量只在stack上__forwording
指针指向自身,如果变量在stack上被copy heap上__forwording
指针指向heap上。
__block int val = 10;
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};
void (^blk)(void) = ^{val = 1;};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_val_0 *val;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
__Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc; }
};
(val->__forwarding->val) = 1; // forwarding指针指向的才是真正存值的地方。
-
block本质是什么?
block会被编译成一个结构体
其中isa是block类型指针,FuncPtr是block代码块函数指针。
根据isa指针,block一共有3种类型的block- _NSConcreteGlobalBlock 全局静态
- _NSConcreteStackBlock 保存在栈中,出函数作用域就销毁
- _NSConcreteMallocBlock 保存在堆中,retainCount == 0销毁
-
block何时会被copy到heap上
block和__block变量都是在stack上创建的。ARC模式下在大多数情况block会被copy到heap上,比如一个函数返回的block,在ARC模式下会被copy到heap上。以下几种情况编译器无法自动检测copy- block作为函数的参数传递,并且在函数内部没有对参数进行copy
- Cocoa框架里的方法,比如使用的
usingBlock
- GCD接口
runtime
谈谈对runtime的理解
runtime是支持OC动态特性的c运行时库。通过runtime可以动态的解析OC对象,进行消息发送和转发,反射机制,方法交换,关联对象等等。OC里的诸多特性都是通过runtime实现的。-
+load
和+initialize
的区别
(1)+initialize
是通过objc_msgSend
进行调用的,所以有以下特点:- 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
- 如果分类实现了+initialize,就覆盖类本身的+initialize调用,也不能说是真正的覆盖,只不过是放到原类方 法的前面去了
- 第一次用的时候才会调用,调用时机比
+load
晚
(2)+load
是直接通过指针调用的,是在runtime加载时就调用,无论你用不用它都会调用
如何使用runtime hook一个class的某个方法,又如何hook某个instance的方法?hook的时候应该注意哪些?
(1)method swizzling通过改变类的dispatch table让方法调用在运行时改变
(2)通过class_getInstanceMethod
获取实例方法,通过class_getClassMethod
获取类方法
(3)先直接为selector添加方法实现,如果不成功在进行方法交换
(4)+load
在class初次加载时被调用,+initialize
在类或实例的方法第一次调用时被调用。因为method swizzling是影响全局状态的,所以应该尽早被加载,并且只加载一次。
(5)dispatch_once
防止多个线程同时调用+load
,确保只swizzling一次。
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
-
isa指针的作用?
runtime如何通过selector找到对应的IMP地址
a class (Class) maintains a dispatch table to resolve messages sent at runtime; each entry in the table is a method (Method), which keys a particular name, the selector (SEL), to an implementation (IMP), which is a pointer to an underlying C function.
https://nshipster.com/method-swizzling/消息转发机制?
见https://www.jianshu.com/p/f26413dec8f0 1.4
当一个消息没有实现时,会抛出unrecognized selector
异常,在这之前有3次补救机会
resolve:为当前实例或类添加方法实现
forwardingForTarget:把方法转发给一个对象
forwardingForInvocation:把方法转发给多个对象
runloop
- 谈谈对runloop的理解
- RunLoop 的基本概念,它是怎么休眠的?
- Runloop和线程的关系?
- Runloop的作用?RunloopMode的原理?
AutoreleasePool
- autoreleasepool的使用场景和原理,什么时候释放
KVO & KVC
关于KVO的基础先参考https://www.jianshu.com/p/2b500fc772a9 第一部分
- KVO、Notification、delegate各自的优缺点,效率还有使用场景
(1) delegate,网络回调,view事件代理等
- 语法严格不易出错
- 一个类可以实现多个代理
- 逻辑流程好监控
- 需要些大量代码
(2)Notification,广播状态,全局通知 - 消息中心发出消息,多个对象可以注册成观察者,方便解耦
- 编译时不会检查通知是否被正确处理
- 管理通知的生命周期
(3)KVO,model-view同步 - 通过key path方式观察对象的属性值
- 接口烂
delegate比NSNotification效率高,代理者需要给个结果给被代理的对象,根据结果做出行动,所以delegate模式解耦并不彻底。通知模式只管发出通知,不考虑接受者如何处理通知,这种通信是双盲的。delegate只是一对一,而这两个可以一对多。这两者也有各自的特点。
https://www.cnblogs.com/tianglin/p/3552555.html
- 如何手动通知KVO
先关闭自动通知,然后实现在setter方法里添加willChangeValueForKey
和didChangeValueForKey
+ (BOOL)automaticallyNotifiesObserversForName
{
return NO;
}
- (void)setName:(NSString *)name
{
if (_name != name) {
[self willChangeValueForKey:@"name"];
_name == name;
[self didChangeValueForKey:@"name"];
}
}
在KVO中,他是怎么知道监听的对象发生了变化?
会通过willChangeValueForKey
和didChangeValueForKey
方法发出通知。KVC机制是如何通过key找到value。
通过先把key转换为getter方法,如果没有getter方法,就会去实例变量里找key对应的合适的变量名称(包括key
,isKey
,_key
,_isKey
),如果找不到就抛出异常。
其他
- 要用什么方式实现多继承?
(a) 组合,像manager那样组合其他类的功能
(b) Protocol,把共用的功能做成protocol
(c) 消息转发,
(d) category
(e) NSProxy - category和extension有什么区别
- NSObject, id区别
id 表示void指针,可以指向任何对象,NSObject表示NSObject指针。在OC里面区别不大
UI
界面多个网络请求,如何处理刷新的?
这考察的是一种是多个请求结束后统一操作,可以通过dispatch_semaphore
,dispatch_group
,NSOperationQueue
实现
(https://cloud.tencent.com/developer/article/1334786)-
说说事件响应链
(1)iOS事件响应机制主要有两方面:事件分发和响应者链。事件分发是寻找第一响应者,APP接收到一个事件后UIKit会自动把事件转发给最合适的responder,这就是the first responder。响应者链用来确定响应事件的responder,如果the first responder不响应事件,那么UIKit就会把事件发送给next responder处理,直到事件被处理事件响应才结束。
(2)通过hit-test机制递归寻找合适的响应者,把寻找过程中responder倒置就是响应者链。
(3)所谓不合适包括以下几种情况- 响应点不在区域内
- 响应者
hidden = YES
- 响应者
alpha < 0.01
- 响应者
userInteractionEnabled = NO
对于image加载的优化方案有哪些
(1)降采样使用缩略图
(2)异步处理图片的解码
(3)使用Image Asset Catalogs
(4)图片解码后缓存-
说说Cell重用原理
当tableview创建时,当前页面上所有的cell会被创建。当滚动列表时,部分cell会移出窗口,移出的cell会被放入一个重用队列里等待重用。滚动时需要获取cell时,会先从重用队列里取cell,如果重用队列里没有cell才会创建新cell。有的tableview里有有多种类型的cell,这时需要identifier来标识,以便知道重用队列里的cell类型。重用cell有两种写法。- 判断型
- 注册型
// 判断型
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//1.定义一个cell的标识为ID
static NSString *ID = @"HomeCell";
//2.从缓存池中取出cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//3.如果缓存池中没有cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
//4.设置cell属性,
cell.textLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];
return cell;
}
// 注册型,如果缓存池中没有,就根据注册创建新的cell,
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID]; // 也可以在xib里注册
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
cell.textLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];
return cell;
}
https://www.jianshu.com/p/5b0e1ca9b673
https://www.jianshu.com/p/e00896730cca
- UITableView的相关优化
UIView 优化可以从两个角度思考CPU和GPU
1)CPU资源消耗原因- 对象创建:减少对象创建cell复用,尽量使用轻量对象如CALayer,对象延迟创建懒加载
- 对象调整:UIView和CALayer修改属性时会通过消息转发机制临时创建方法,消耗较大。尽量避免在滚动时修改视图属性。
- 对象销毁:
- 布局计算与Autolayout:后台线程提前计算好视图布局、并且对视图布局进行缓存,尽量一次性计算好布局,不要在滚动时动态调整。对复杂页面不要进行Autolayout,应该自己计算frame。
- 文本计算与渲染:CPU会计算文本宽高并绘制成bitmap,如果文本内容很多CPU压力会很大,使用TextKit或CoreText对文本进行异步绘制避免主线程压力过大
- 图片解码与渲染 :图片在交给CALayer.contents 时会进行解码,并生成一张bitmap。为了避免主线程压力过大,可以把图片解码和渲染过程放在异步线程进行,并且进行图片缓存。使用SDWebImage,在drawRect里异步绘制。
2)GPU 资源消耗原因,GPU渲染管道主要提供服务有GPU 能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,然后输出到屏幕上。 - 纹理的渲染:避免使用大图,使用缩率图。
- 视图的混合:应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成。也可以把多个layer预先合成一个layer
- 图形的生成:圆角,遮罩,阴影等会导致离屏渲染
- cell复用
- 设置预估行高,预先缓存动态行高
- 减少Subviews层级、异步绘制、避免离屛渲染、使用hidden隐藏图层
- 分页加载数据,预先异步请求数据
- 滑动TableView时,按需加载内容
- 使用runloop优化卡顿
- 在willDisplayCell:forRowAtIndexPath:代理方法中绑定数据
http://zeroyuan.com/2016/04/29/2016-04-29-ios[nil]uitableviewxing-neng-you-hua/
https://blog.csdn.net/hmh007/article/details/54907560
https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
-
UIViewController 生命周期
-
如果页面 A 跳转到 页面 B,A 的 viewDidDisappear 方法和 B 的 viewDidAppear 方法哪个先调用?
在每一种转场下,appear 与 disappear 都有一些不一样的顺序,
UIView和CALayer之间的关系
- UIView继承自UIResponder,负责管理显示内容和响应事件,UIView显示内容是通过CALayer来实现的
- CALayer主要负责内容展示,动画实现。
- 每一个UIView都有一个CALayer属性,而UIView作为该CALayer的delegate可以实现显示、绘制、布局和动画方法。
@protocol CALayerDelegate <NSObject>
@optional
- (void)displayLayer:(CALayer *)layer;
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
- (void)layoutSublayersOfLayer:(CALayer *)layer;
- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;
UIView、CoreAnimation和CoreGraphics的关系
参考https://www.jianshu.com/p/509ef033cb1d iOS UI优化 第四小节用过 TableView 吗,平时怎么解决 TableView 滑动卡顿问题的?
同5 tableview的优化
Memory
- 介绍下内存的几大区域?
(a) Stack:由系统存放函数参数和局部变量的值,系统管理内存分配和释放
(b) Heap: 手动分配的内存空间,程序员负责管理内存
(c) 全局区:存放全局变量和静态变量,由系统管理
(d) 常量区:存放常量字符串,由系统管理
(e) 代码区:存放二进制代码指令 - app内存你是如何分析的?
Thread
- 谈下iOS开发中知道的哪些锁? 哪个性能最差?SD和AFN使用的哪个? 一般开发中你最常用哪个? 哪个锁apple存在问题又是什么问题?
-
常见的锁及其性能
- NSLock 内部封装了一个 pthread_mutex, NSCondition装了一个互斥锁和条件变量, @synchronized是一个 OC 层面的锁OC 在底层使用了一个互斥锁的数组。
- AFN的URLSessionManager里使用了NSLock,在存取task delegate字典时使用的。SD里使用的dispatch_semaphore保证http headers和url operation存取的线程安全。
- OSSpinLock会有优先级反转问题,由于spin lock是忙等机制,当低优先级线程拿到锁,高优先级线程进入忙等状态,低优先级线程锁不释放,高优先级就一直忙等。
- 使用atomic一定是线程安全的吗?
使用atomic只能保证getter和setter操作的原子性,即当一个线程访问getter时其他线程不会访问该方法。如果此时有其他线程访进行setter方法,那么getter获得的值就不确定了。所以atomic并不是线程安全的。(或者说getter和setter使用的不是同一把锁) - GCD原理
(1)GCD是一个异步任务派发框架,提供了系统级线程管理服务。
(2)GCD中队列分为串行队列和并行队列。GCD提供5个系统队列,一个主队列和4个不同优先级的后台队列 - GCD控制线程数量和任务依
dispatch_semaphore, dispatch_group
Third Framework
SDWebImage
- 分析下SDWebImage
- SDMemoryCache继承自NSCache并且遵循SDMemoryCache协议,以key-value形式存取信息
- SDWebImage 的最大并发数
- SDWebImageDownloader里用NSOperation和NSOperationQueue来管理下载任务。
- 用SDWebImageDownloaderConfig存储downloader的各种设置,其中maxConcurrentDownloads表示最大并发数,默认的config使用的是6
- 配置: config
_downloadQueue.maxConcurrentOperationCount = _config.maxConcurrentDownloads;
- SDWebImage是如何区分不同格式的图像的
根据文件二进制编码的第一字节确定图片类型, 在NSData+ImageContentType
中有如下实现。在https://www.garykessler.net/library/file_sigs.html 网页里可以找到各种类型文件的头部字节。
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
if (!data) {
return SDImageFormatUndefined;
}
// File signatures table: http://www.garykessler.net/library/file_sigs.html
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return SDImageFormatJPEG;
case 0x89:
return SDImageFormatPNG;
case 0x47:
return SDImageFormatGIF;
case 0x49:
case 0x4D:
return SDImageFormatTIFF;
case 0x52: ...
break;
}
case 0x00:...
break;
}
}
return SDImageFormatUndefined;
}
- 如何保证UI操作放在主线程执行
通过dispatch_main_async_safe
保证在主线程执行,可以减少线程切换。可以通过非主队列派发任务到主线程执行,因此用isMainThread
方式判断并不安全。
#define DISPATCH_CURRENT_QUEUE_LABEL NULL
dispatch_queue_get_label(dispatch_queue_t _Nullable queue); // 获取queue的label,如果传如NULL则获取当前queue的label
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\
if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif
- 如何保证
SDImageCache
的线程安全
-
SDMemoryCache
继承者NSCache
,NSCache
是线程安全的 -
SDDiskCache
使用NSFileManger
管理磁盘文件的创建和删除文件,用NSData读取文件。磁盘缓存相关操作会被放入ioQueue串行队列里保证线程安全。
- 使用SDWebImage内存爆涨的问题遇到没,怎么解决?
RAC
- ReactiveCocoa(RAC)如何防止UIButton短时间内多次重复点击,大概思路?
结合RACCommand使用。
https://www.zybuluo.com/myron-lee/note/430599 - RAC中数据视图绑定方式
- RACSignal: 1v1单向数据流
- RACMulticastConnection: 1v多单向数据流
- RACChannel: 1v1双向数据流
- RACChannel如何实现双向绑定
- RACCommand如何实现
- RAC内存管理
https://www.jianshu.com/p/d774d4680c5d
AFNetworking
- AFNetworking是否支持ipv6?
- 与 NSURLConnection 相比,NSURLsession 改进哪些?
YYKit
- YYAsyncLayer如何异步绘制?
CocoaPod
- 熟悉 CocoaPods 么?能大概讲一下工作原理么?
cocoapods主要由依赖解析器、下载工具和Xcode工程管理工具这三个组件构成,我们创建pod时把pod的描述信息放到podspec文件里,在使用的时候通过podfile指定依赖关系,cocoapods就会自动对依赖关系进行解析,并下载对应的源码,配置Pods工程。
WorkFlow
- 测试都有哪些方式?优缺点呢?
Common
NSURLSession
session和cookie
HTTP协议本身是无状态的,在进行连接-请求-响应-关闭之后就结束了。 cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。
理解HTTP session原理及应用URLSession类型
- shareSession: 使用当前全局设置的
NSURLCache
,NSHTTPCookieStorage
andNSURLCredentialStorage
- Default Sessions: 采用
defaultSessionConfiguration
, - Ephemeral sessions: 采用
ephemeralSessionConfiguration
,不允许 write caches, cookies, or credentials to disk. - Background Sessions: APP不运行的时候,开启后台上传或下载任务
- URL Session Tasks类型
- Data tasks:使用NSData对象接发数据,通常用于短小高频请求
- Upload tasks:通常以文件的形式发送数据,支持后台上传
- Download tasks:支持文件形式接收数据,支持后台上传下载
- URL sessions also support canceling, restarting, resuming, and suspending tasks, and provide the ability to resume suspended, canceled, or failed downloads where they left off.
task可以被cancel,也可以被suspend。调用resume,task会重启任务。
- 缓存策略
- 为每个request设置
requestCachePolicy
- 在sessionConfiguration里设置
requestCachePolicy
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
NSURLRequestUseProtocolCachePolicy = 0, // 使用缓存的方式由 Cache-Control 决定
NSURLRequestReloadIgnoringLocalCacheData = 1,
NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,
NSURLRequestReturnCacheDataDontLoad = 3,
NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};
- 设置全局cache大小
- 在sessionConfiguration里设置cache大小
NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1*1024*1024 diskCapacity:30*1024*1024 diskPath:nil];
NSURLCache.sharedURLCache = urlCache;
NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultConfiguration.URLCache = urlCache;
Crash
-
导致crash的场景
(1)iOS策略- 低内存
- watchdog
- 用户强退
(2)代码bug - 数组越界
- KVC,给不存在的key设置value
- 访问野指针(EXC_BAD_ACCESS)
- 未识别selector
- 多线程,死锁,子线程跟新UI,
- Socket长连接,进入后台没有关闭
https://www.jishuwen.com/d/2del#tuit
-
crash的收集和定位bug的方式谈下
(1)简介
先在最底层产生Mach异常;Mach异常在host层被转换为相应的Unix Signal; 在OC层如果有对应的NSException(OC异常),就转换成OC异常,OC异常可以在OC层得到处理。
(2)捕获crash- try catch 捕获OC异常,APPLE不建议这么做。所以通常OC层中未被捕获的异常,通过注册NSUncaughtExceptionHandler捕获异常信息
- OC中层不能转换的Mach异常,利用Unix标准的signal机制,注册SIGABRT, SIGBUS, SIGSEGV等信号发生时的处理函数。
(3)崩溃日志 - Xcode里获取崩溃日志
- 三方获取崩溃日志
- 终端获取崩溃日志
通过查看日志里的Exception type、Exception code和堆栈信息定位bug。在Xcode里可以通过全局断点定位bug。
https://hanson647.com/2018/07/25/iOS%E4%B8%AD%E7%9A%84crash/
dSYM你是如何分析的?
dSYM是符号表,保存了地址和符号之间的映射关系。通过UUID可以找到对应的dSYM,使用符号表工具,输入内存地址可以得到对应的符号。0x8badf00d表示是什么?
eat bad food,watchdog长时间卡顿如何定位野指针bug
(1)Enable Scribble: Fill allocated memory with 0xAA and deallocated memory with 0x55.
Scribble will make it rather obvious that you're using a memory block after it's free'd by overwriting any data that used to be in the memory block upon free.
启用数据写入:对象释放时,所用内存并没有完全被擦除,仍有旧对象部分数据可用,如果不使用Zombie调试,App可能不会直接crash。对付这种情况,其实很简单,可以在对象内存释放时写入无意义数据分配的内存写入数据0xAA,释放的内存写入0x55。有些内存块已经被释放时,可以通过对数据进行重写让你继续使用内存块。
(2)僵尸对象(Zombie Objects):在对象释放(retainCount 为0)时,使用一个内置的Zombie对象,替代原来被释放的对象。无论向该对象发送什么消息(函数调用),都会触发异常,抛出调试信息。
其他
- framework是动态链接库还是静态链接库,和.a的区别是什么
- 库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用。
- 静态库即静态链接库(Windows 下的 .lib,Linux 和 Mac 下的 .a),动态库即动态链接库(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib/.tbd)
- Framework 实际上是一种打包方式,将库的二进制文件,头文件和有关的资源文件打包到一起,方便管理和分发。
https://www.jianshu.com/p/13bf46df9387
- app的性能优化,都有哪些?优化你是从哪几方面着手?
CPU,Memory,UI,Networking - 是否了解设计模式, 用过哪些?
- 观察者模式
- 工厂模式,类族
- 组合模式,manager
- 状态机,NSOperation
-
MVC里面, View怎么通知到Model?
原则上view和model不能直接通信,需要通过controller来通信。controller同时持有view和model,model通过KVO或通知向controller发信息,view通过delegate和target-action与controller通信。
做过最大的项目主要难点在哪里?
MVC具有什么样的优势,各个模块之间怎么通信,比如点击 Button 后 怎么通知 Model?
同 4,点击button后,通过target-action,controller处理点击事件更新model。CoreData的使用,如何处理多线程问题?
-
如何组件化解耦的?
模块之间互相依赖,代码耦合性强,可维护性差,为了解决这个问题,从以下几个方面着手:
(1)分层,把整个项目分为,分层的原则是底层不依赖于上层,上层公共部分下沉到底层- 基础层:基础模块,网络模块,存储模块,
- 服务层(能力层):分享服务,网络接口,设备扫描,设备接入
- 业务层:按页面功能划分
- 壳工程:集成模块使用
(2)通信,主要有3中方式 - url-block:
- url-protocol
- target-action
(3)管理协作,通过cocoa pod
https://juejin.im/post/5c55af09f265da2d8f470ede
https://wereadteam.github.io/2016/03/19/iOS-Component/
https://github.com/casatwy/CTMediator
https://github.com/alibaba/BeeHive
怎么防止别人反编译你的app?
代码文件编译生成过程,做了哪些事情;build过程
针对工程中的每个 target,Xcode 都会执行一系列的操作,将相关的源码,根据所选定的平台,转换为机器可读的二进制文件。
(1)依赖target处理:先处理pch预编译头文件,然后编译生成不同架构的.a文件并合并
(2)编译项目代码
(3)链接
(4)将静态资源copy到app bundle中
(5)code sign,packaging
https://objccn.io/issue-6-1/
[https://hanson647.com/2018/01/22/2017/iOS%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B%E8%AF%A6%E8%A7%A3/]app启动做了哪些事情;app启动优化
- 说说你项目中常用到的调试技巧?
- 如何把异步线程转换成同步任务进行单元测试?
- UIView和NSObject这两个类,所有里面的方法和原理都需要了解一下。
算法与数据结构
- 两个无限长度链表(也就是可能有环) 判断有没有交点
- 字典的工作原理 ?怎100w个中是怎么快速去取value?
- 有序和无序set实现原理区别
- 深度遍历和广度遍历使用场景
- 如何实现一个数组每个元素依次向右移动k位。(后头的往前面补) 比如: [1, 2, 3, 4, 5] 挪两位变成[4, 5, 1, 2, 3]