iOS-face

iOS知识点汇总

2020-11-30  本文已影响0人  桔子橙子柚子_F

UI试图

UITableView相关

1.cell的复用
2.数据源同步:

事件传递&视图响应

UIView提供内容,以及负责处理触摸等事件,参与响应链
CALayer负责显示内容的contents

图像显示原理

CPU和GPU是通过总线连接起来的,CPU输出的是位图,再经由总线在合适的时机上传给GPU,GPU拿到位图后会做图层的渲染、纹理的合成,之后把结果放到帧缓冲区中,由视频控制器根据VSync信号在指定时间之前去提取帧缓冲中的屏幕显示内容,最终显示到屏幕上。


image.png
image.png
UI卡顿、掉帧

原因:1s完成60帧的刷新,在视觉上是流畅的,也就是每个VSync之间有16.7ms,在这16.7ms之间,要CPU和GPU共同完成下一帧画面的合成,不能完成则会导致卡顿(掉帧)


image.png

界面滑动优化方案:

UI绘制原理/异步绘制

调用UIView的setNeedsDisplay方法,并不会立刻发生当前视图的绘制工作,而是先调用layer的setNeedDisplay方法,相当于在当前layer上打上一个脏标记,然后在当前RunLoop将要结束前调用CALayer的display方法,去绘制试图


绘制原理.png

异步绘制:
layer.delegate实现了displayLayer:方法,进入异步绘制流程中

离屏渲染

在屏渲染:GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行的
离屏渲染:GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。当我们设置UI视图的图层属性时(圆角,遮罩,阴影),如果指定为在未预合之前(无法把渲染结果直接写入帧缓存区,而是暂存在另外的内存区域之后再写入frame buffer)不能用于直接显示的时候,就会触发离屏渲染。
为何要避免:会创建新的渲染缓冲区,GPU会产生额外的开销,很有可能导致CPU和GPU绘制一帧的时间超过16.7ms,会造成卡顿和掉帧

OC语言

分类Category
关联对象

可以为分类添加实例变量
objc_getAssociatedObject(id object, const void *key)
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
objc_removeAssociatedObjects(id object)

扩展

扩展Extension

@interface XXX ()
//私有属性
//私有方法(如果不实现,编译时会报警,Method definition for 'XXX' not found)
@end
代理

是一种软件设计模式
传递方式是一对一的

NSNotification
KVO
KVC

如果没有找到Set方法的话,会先根据accessInstanceVariablesDirectly返回实例变量是否存在,YES会按照_key,_iskey,key,iskey的顺序搜索成员并进行赋值操作,都没有会调用valueForUndefinedKey:

属性关键字

Runtime

对象、类对象、元类对象

对象,指向类对象
类对象,指向元类对象

消息传递机制
消息传递.png
消息转发流程
Method-Swizzling
动态添加方法
class_addMethod(Class cls, SEL name, IMP imp, const char * types)

内存管理

内存布局

stack:方法调用
heap:通过alloc等分配的对象

ARC

自动引用计数管理内存
ARC是编译器(自动插入retain、release)和Runtime共同协作
ARC中禁止手动调用retain/release/retainCount/dealloc(ARC下可以重写dealloc方法,但是不能显示调用super dealloc)
新增weak、strong属性关键字

MRC

手动引用计数(斜体方法在ARC中调用会编译报错)
alloc:分配对象的内存空间
retain:引用计数+1
release:引用计数-1
retainCount:当前对象的引用计数值
autorelease:autoreleasePool结束时调用release
dealloc:显示调用[super dealloc]

引用计数机制
//通过哈希查找对象引用计数相关的sidetable,从sidetables中找到sidetable
SideTable& table = SidTables()[this];
//获取引用计数map,通过哈希查找从中获取引用计数值
size_t& refcntStorage = table.refcnts[this];
//引用计数+1,实际是加偏移量4,反应出的结果是+1
refcntStorage += SIDE_TABLE_RC_ONE;
SideTable& table = SidTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
//引用计数-1
it->sencond -= SIDE_TABLE_RC_ONE;
SideTable& table = SidTables()[this];
size_t refcnt_result = 1;
RefcountMap::iterator it = table.refcnts.find(this);
//alloc没有给对象进行+1,由于refcnt_result为1,所以调用retainCount为1
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
弱引用表
objc_initWeak() -> storeWeak() -> weak_register_no_lock()
dealloc() -> clearDeallocating() -> weak_clear_no_lock()
AutoReleasePool

编译器会将@autorealsepool{}改为

void *ctx = objc_autoreleasePoolPush(); (-> AutoreleasePoolPage::push)
{}代码块
//一次pop相当于一次批量的pop操作
objc_autoreleasePoolPop(ctx);(-> AutoreleasePoolPage::pop)
循环引用

自循环引用
相互循环引用
多循环引用

Block

Block本质-对象

Block是将函数及其执行上下文封装起来的对象

截获变量特性
__block修饰符的本质

一般情况下,对被截获变量进行赋值操作需要添加__block修饰符(赋值 != 操作)
__block修饰的变量变成了对象

__block int xxx;
struct _Block_byref_XXX_0 {
    void *isa;
    //栈上的__forwarding指针指向自身
    //栈上的block进行copy操作后,栈上的__forwarding指向堆上的block,堆上的__forwarding     指向自身
    _Block_byref_XXX_0 *__forwarding;  
    int __flags;
    int __size;
    int XXX;
}
Block的内存管理

GlobalBlock(放在已初始化数据区中)
StackBlock(被销毁时,__block修饰的对象和block都会被销毁)
MallocBlock(堆上的block)

循环引用

__weak修饰

多线程

GCD
NSOperation/NSOperationQueue
NSThread
start() -> 创建pthread -> main() -> [target performSelector:selector] -> exit()
互斥锁、自旋锁、递归锁等
struct semaphore {
  int value;
  List <thread>;
}
{
  S.value = S.value - 1;
  //阻塞是一个主动行为
  if S.value < 0 then Block(S.List)
}
{
  S.value = S.value + 1;
  //唤醒是一个被动行为
  if S.value >= 0 then wakeup(S.List);
}

RunLoop

什么是RunLoop

RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
没有消息处理时,休眠以避免资源占用(用户态,通过系统调用进入内核态)
有消息处理时,立刻被唤醒(内核态 -> 用户态)
不是个简单的while循环,主要是发生了状态切换

数据结构

NSRunLoop是对CFRunLoop的封装,提供了面向对象的API

struct __CFRunLoop {    
    pthread_t _pthread;  //对应的线程  RunLoop和线程一一对应
    CFMutableSetRef _commonModes; //多个commonModes <String>
    CFMutableSetRef _commonModeItems;   //多个Observer,多个Timer,多个Source
    CFRunLoopModeRef _currentMode;  //当前运行的mode
    CFMutableSetRef _modes; //多个modes <CFRunLoopMode>
};
struct __CFRunLoopMode {
    CFMutableSetRef _sources0;  //需要手动唤醒当前线程
    CFMutableSetRef _sources1;  //具备唤醒线程的能力
    CFMutableArrayRef _observers;   //
    CFMutableArrayRef _timers;   //
};
各数据结构间的关系
事件循环机制

有事做事,没事做时休息


image.png
RunLoop和NSTimer

用户滑动ScrollView时,RunLoop会转到UITrackingRunLoopMode,Mode发生切换,timer不会再生效了

RunLoop与线程之间的关系

线程与RunLoop是一一对应的
自己创建的线程默认是没有RunLoop的

常驻线程

1、为当前线程开启一个RunLoop
2、向该RunLoop中添加一个Port/Source等维持RunLoop的时间循环
3、启动该RunLoop

保证子线程数据回来更新UI的时候不打断用户的滑动操作

可以把更新UI放到DefaultMode下,这样不会在TrackingMode下更新UI

网络

HTTP协议

超文本传输协议

HTTPS与网络安全
TCP/UDP
DNS解析

域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

Session/Cookie

对HTTP协议无状态特点的补偿
Cookie:保存在客户端,用来记录用户状态

设计模式

六大设计原则
责任链

实际需求:对于业务A -> 业务B -> 业务C 变更为 业务C -> 业务B -> 业务A
业务C的nextBusinessObject为业务B,业务B的nextBusinessObject为业务C


类构成
桥接
适配器
单例
命令

架构/框架

模块化
分层
解耦
降低代码重合度

图片缓存框架
image.png
阅读时长统计框架
image.png
复杂页面架构
客户端整体架构
image.png

算法

字符串反转
链表反转
有序数组合并
Hash算法
查找两个子视图的共同父试图
求无序数组当中的中位数

三方库

AFNetworking
框架图
SDWebImageView
架构简图
ReactiveCocoa
上一篇 下一篇

猜你喜欢

热点阅读