https://www.jianshu.com/p/7c44c3b2a056
一.@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?
答:使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。
v二.ViewController生命周期
按照执行顺序排列:
initWithCoder:通过nib文件初始化时触发。
awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。
loadView:开始加载视图控制器自带的view。
viewDidLoad:视图控制器的view被加载完成。
viewWillAppear:视图控制器的view将要显示在window上。
updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
viewDidAppear:视图控制器的view已经展示到window上。
viewWillDisappear:视图控制器的view将要从window上消失。
viewDidDisappear:视图控制器的view已经从window上消失。
三,反射机制
1.通过类名的字符串形式实例化对象。(NSClassFromString)
- 将类名变为字符串。NSString className = NSStringFromClass(class);
3.通过方法的字符串形式实例化方法。SEL selector = NSSelectorFromString(@"setName");
4.将方法变成字符串。NSStringFromSelector(@selector(setName:));
四,runtime
runtime是oc的底层,一套语言的api,用runtime获取某个类的所有属性(私有属性、非私有属性),在获取到某个类的属性后就可以对该属性进行访问以及修改了。.runtime常用的有:
1.给分类添加属性
objc_setAssociatedObject objc_getAssociatedObject
2.方法交换 比方说 给image加载图片添加一个成功失败啥的
+ (void)load {
//1,先获取imageNamed方法地址
// class_getClassMethod(获取某个类的方法)
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
// 2. 获取自定义的my_imageNamed方法地址
Method my_imageNamedMethod = class_getClassMethod(self, @selector(my_imageNamed:));
// 3. 交换方法地址, 相当于交换实现
method_exchangeImplementations(imageNamedMethod, my_imageNamedMethod);
}
3.消息转发
4.反射机制(解偶)
五。什么闭包
闭包就是获取其它函数局部变量的匿名函数。
如果需要在block内部改变外部栈区变量的话,需要在用__block修饰外部变量。
六,浅拷贝深拷贝
浅拷贝就是拷贝后,并没有进行真正的复制,而是复制的对象和原对象都指向同一个地址
深拷贝是值拷贝,生成一个新的对象指向不同的地址
copy:对于不可变对象为浅拷贝, 对于可变对象为深拷贝,
mutableCopy:始终是深拷贝
七 RunLoop
RunLoop是一种运行循环机制,其实就是死循环。它有两个作用:1.保证程序不退出 2.负
责监听事件。我们可以理解为Runloop在不断的循环中,一直在询问线程队列中是否有待办任务。如果有任务就去处理,没有任务就休息。
内部其实是一个do while循环,这也正是Runloop运行的本质
Thread默认是没有对应的RunLoop的,仅当主动调用Get方法时,才会创建
所有Thread线程对应的RunLoop被存储在全局的__CFRunLoops字典中。同时,主线程在static CFRunLoopRef __main,子线程在TSD中,也存储了线程对应的RunLoop,用于快速查找。
image.png
需要使用port或input source和别的线程通信(基本用不到)
在当前线程需要使用NSTimer
在Cocoa程序中需要使用performSelector… 系列函数
需要子线程保活来执行后台任务
@autoreleasepool {
}
RunLoop的Mode
关于Mode首先要知道一个RunLoop 对象中可能包含多个Mode,且每次调用 RunLoop 的主函数时,只能指定其中一个 Mode(CurrentMode)。切换 Mode,需要重新指定一个 Mode 。主要是为了分隔开不同的 Source、Timer、Observer,让它们之间互不影响。
image.png八 Cocoapods 的工作流程
- 查看 ~/.cocoapods/repo/master/Specs 是否存在
- 存在,从这个本地三方库信息库中获取 Podfile 中对应三方库的 git 地址
- 不存在,输出 Setting up CocoaPods Master repo,并拉取三方库信息库到 ~/.cocoapods/repo/中* 使用 git 命令从 GitHub 上拉取 Podfile 中对应的三方库源码
九 响应者链
响应者链就是通过UIResPonder的属性链接起来的view和controller,但一个view添加到他的父试图上时,他的nextUIResPonder就指向他的父视图,形成一条链
十 点击发生了什么
1.当iOS程序发生触摸事件后,系统会利用Runloop将事件加入到UIApplication的任务队列中
2.UIApplication分发触摸事件到UIWindow,然后UIWindow依次向下分发给UIView
3.UIView调用hitTest:withEvent:方法看看自己能否处理事件,以及触摸点是否在自己上面。
4.如果满足条件,就遍历UIView上的子控件。重复上面的动作。直到找到最顶层的一个满足条件
十一 hitTest:withEvent:的处理流程
1.首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
2.若返回NO,则hitTest:withEvent:返回nil;
3.若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕
4.若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
5.如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。
十二 UIView为CALayer提供内容,以及负责处理触摸等事件,参与响应链
CALayer负责显示内容contents
十三 view和layer
UIView为CALayer提供内容,以及负责处理触摸等事件,参与响应链
CALayer负责显示内容contents
十四 图像显示原理
的图像经过CRT电子枪以极快的速度一行一行的扫描,扫描出来就呈现了一帧画面,随后电子枪又会回到初始位置循环扫描,形成了我们看到的图片或视频。为了让显示器的显示跟视频控制器同步,当电子枪新扫描一行的时候,准备扫描的时发送一个水平同步信号(HSync信号),显示器的刷新频率就是HSync信号产生的频率。然后CPU计算好frame等属性,将计算好的内容交给GPU去渲染,GPU渲染好之后就会放入帧缓冲区。然后视频控制器会按照HSync信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器,就显示出来了。这里只是简作描述,专业描述请自行查询
1.CPU:输出位图
2.GPU :图层渲染,纹理合成
3.把结果放到帧缓冲区(frame buffer)中
4.再由视频控制器根据vsync信号在指定时间之前去提取帧缓冲区的屏幕显示内容
5.显示到屏幕上
十四 CPU工作
1.Layout: UI布局,文本计算
2.Display: 绘制
3.Prepare: 图片解码
4.Commit:提交位图
十五 UI卡顿掉帧原因
CPU会去计算屏幕要显示的内容,之后将计算好的内容提交到GPU去渲染。随后,GPU将渲染结果提交到帧缓冲区,等到下一个VSync到来时将缓冲区的帧显示到屏幕上。一帧的显示是由CPU和GPU共同决定的。一般来说,页面滑动流畅是60fps,也就是1s有60帧更新,即每隔16.7ms就要产生一帧画面,而如果CPU和GPU加起来的处理时间超过了16.7ms,就会造成掉帧甚至卡顿。
滑动优化方案
CPU:把以下操作放在子线程中
1.对象创建、调整、销毁
2.预排版(布局计算、文本计算、缓存高度等等)
3.预渲染(文本等异步绘制,图片解码等)
十六 离屏渲染
GPU屏幕渲染有两种方式:
(1)On-Screen Rendering (当前屏幕渲染)
指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。
(2)Off-Screen Rendering (离屏渲染)
指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。
离屏渲染的代价是很高的 创建新缓冲区 上下文切换
既然离屏渲染这么耗性能,为什么有这套机制呢?
有些效果被认为不能直接呈现于屏幕,而需要在别的地方做额外的处理预合成。图层属性的混合体没有预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染。屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。
下面的情况或操作会引发离屏渲染:
- 为图层设置遮罩(layer.mask)
- 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
- 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
- 为图层设置阴影(layer.shadow *)。
- 为图层设置layer.shouldRasterize=true
- 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层
- 文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
- 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。
https://www.jianshu.com/p/cff0d1b3c915
十七,自动释放池的原理:
排布在“栈”中,对象执行autorelease消息后,系统将其放入最顶端的池里(进栈),而清空自动释放池就是把对象销毁(出栈)。而调用出栈的时机:就是当前线程执行下一次事件循环时。
十八 谓词
NSPredicate类是用来定义逻辑条件约束的获取或内存中的过滤搜索。
可以使用谓词来表示逻辑条件,用于描述对象持久性存储在内存中的对象过滤。其实意思就是:我是一个过滤器,不符合条件的都滚开。
十九 Category 为什么不能添加实例变量
通过结构体 category_t ,我们就可以知道,在 Category 中我们可以增加实例方法、类方法、协议、属性。这里没有 objc_ivar_list 结构体,代表我们不可以在分类中添加实例变量。
因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这个就是 Category 中不能添加实例变量的根本原因。
网友评论