当前位置:网站首页>iOS大厂面试查漏补缺
iOS大厂面试查漏补缺
2022-07-31 05:44:00 【JH_Cao】
一、UI方面
1. UI卡顿问题
- 原因
- 屏幕垂直信号之间是一帧率,16.7ms
- CPU计算,GPU渲染,这两步要在一帧的时间完成。
- 如果没完成,就会出现卡顿,掉帧情况
- 优化方案
- CPU层面:
- 尽量用更加轻量级的对象,比如不需要处理事件的视图,可以用CALayer代替UIView
- 图片的解码,可以在子线程用CGBitmapContext绘制,然后从Bitmap中创建图片
- 尽量不要对UIView频繁调用属性修改,frame,bounds,transform等修改
- 尽量提前计算好布局,在有需要的时候一次性调整对应的属性
- Autolayout会比直接设置frame消耗更多的CPU资源,所以尽量直接设置frame
- 图片的size最好刚好跟UIImageView的size保持一致
- 控制线程的最大并发数量
- 对象创建、及时销毁
- 文本等预排版(boudingRectWithSize && drawWithRect 这两个方法)
- GPU层面:
- 避免离屏渲染(避免设置圆角带来离屏渲染:贝塞尔曲线绘制)
- 减少视图层级(Texture/ASDK 就是通过pre-composing技术,提前把多个sub-layer合成渲染为一张图片,减少视图层级,大大提高渲染效率)
二、网络方面
- 网络请求缓存问题?
- 空间:URLCache是苹果提供的网络请求缓存类,主要用于GET方法,请求资源用的。默认内存是512K,磁盘10M,也可以自己修改
- 策略:6种,使用缓存,不使用缓存,实现,不实现等等。
- 更新缓存内容:
Last-Modified:服务器会在资源后面有个标记Last-Modified,表示最后修改时间。第一次请求会缓存这个时间,后面请求时候,发送一个If-Modified-Since报头。如果返回304就不会传输资源数据,用本地缓存的即可。否则会传输资源。Etag:服务器对资源实体用Etag来标记,就是对实体进行散列计算,得到唯一值。第二次请求,对这个值进行比较。如果返回304就数据为空,和上述情况一样。
三、安全方面
- Mach-O
- Mach Object的缩写,是mac及iOS上可执行文件的格式,不一定是可执行,只是一种文件格式。
- 包含类型:
- OBJECT:
.o或.a文件(目标文件)
- OBJECT:
- EXECUTE:指的是IPA拆包后的文件(可执行文件)
- DYLIB:指的是
.dylib或.framework(动态库文件)
- DYLIB:指的是
- DYLINKER:指的是动态连接器(动态库连接器文件)
- DSYM:(符号表),指的是有保存符号信息用于分析闪退信息的文件(符号表文件)
- 加载过程
- 把可执行文件加载到内存中
- 从可执行文件中分析
dyld的路径
- 从可执行文件中分析
- 把
dyld加载到内存中
- 把
dyld递归加载所有的动态连接库dylib
- ldid重签名
- 用
ldid -e命令导出可执行文件的签名文件entitlements
- 用
- 用越狱了的手机,导出
SpringBoard的签名文件entitlements
- 用越狱了的手机,导出
- 把
SpringBoard的签名文件覆盖自己的APP的签名文件entitlements
- 把
四、底层原理
OC为什么要设计Metaclass?
- 这个是要从面向对象设计的哲学去考虑这个问题
- 第一:提取出某一种群体的特性,对这类群体进行划分,从而有基本的
属性成员信息、方法信息
第二:可以复用消息传递,在OC的消息传递过程中,只要是这个群体的实体,就不需要关心自身的类型是什么,只需要关心这个接口。在传递消息的时候就可以复用了。
第三:对于描述这个类的类,即元类,对于类的类方法而言,就类似实例的实例方法,也需要这么一个复用消息传递机制,所以也就产生了针对类的元类,本质上来说,我认为也是消息传递的复用工具
类可以调用对象方法吗?
- 可以,因为基类的super-class指针,指向的是类。
isa的指针指向?
- 实例对象的isa指向类对象,类对象的isa指向元类
- isa指向,在不同平台(arm64 或 x86)通过掩码计算,来得到真实的地址值。
KVO? 键值监听
- 使用:
[self.person addObserver:self forKeyPath:@"age" options:options context:nil];
- 使用:
- 本质:
第一:NSKVONotifying_Person继承Person类
第二:在set方法里,增加了willChange和didChange方法
第三:联想到在swift 里,我们可以写属性监听器
第四:从runtime的角度也可以验证,通过调用methodForSelector方法,查看实际上调用的方法是什么
第五:从runtime调用class_copyMethodList方法,也可以获取添加监听后的类的所有的方法列表,打印就会发现与添加之前不一样了。
第六:从runtime调用object_getClass获取类名,打印也会发现与添加之前不一样
- 本质:
- 手动触发KVO?
第一:只需要手动在合适的地方添加willChange和didChange方法
第二:通过-> 方式赋值,不会调用set方法,不会触发KVO
- 手动触发KVO?
KVC 键值编码
- 使用
[self.person setValue:@10 forKey:@"age"];[self.person setValue:@20 forKeyPath:@"cat.weight"];
- 使用
- 原理
第一:赋值
setKey
_setKey
_key
_isKey
key
isKey
第二:取值
getKey
Key
isKey
_key
- 原理
- 所以,KVC是可以触发KVO的,因为会调用set方法
Category的理解?
- 分类的使用场景:一般业务比较复杂,需要将一部分功能或业务逻辑分模块出来,就可以用分类
- Category的本质
- 通过runtime加载某个类的所有Category数据
- 把所有的Category的方法、属性、协议,的数据,合并到一个大数组中
- 后编译的Category数据,会在数组的前面,所以后编译的Category会被先调用
- 如果分类中含有和原类的类方法同名的,就会先调用分类的方法
- 通过runtime关联属性
objc_setAssociatedObjectobjc_getAssociatedObject- 关联对象并不是存储在被关联的对象中,而是存储在全局统一的AssociationsManager中
- 设置关联对象为nil,就是相当于移除关联对象
load、initialize方法
- 调用顺序
- load
- 先调用类的load(先编译的先调用),再调用分类的load(也是先编译的先调用)。
- 先调用父类,再调用子类。
- initialize
- 先调用父类,再调用子类。
- 调用方式
- load是在系统加载的时候,根据函数地址直接调用的
- initialize是通过消息发送调用
- 调用时间
- load是runtime加载类、分类的时候调用,只会调用一次(在APP冷启动的第二阶段就会调用)
- initialize是类在第一次接收到消息的时候调用,每个类只会initialize一次。但是父类的initialize方法可能会被调用多次。
// JHPerson 和 JHTeacher 都是 Person 的子类,并且只有Person这个父类实现了+initialize方法
- (void)testInit {
NSLog(@"--------");
[JHPerson alloc];
[JHPerson alloc];
[JHTeacher alloc];
[JHTeacher alloc];
}
// 伪代码逻辑
if (JHPerson没有初始化) {
if (JHPerson的父类Person没有初始化) {
objc_msgSend([Person class], @selector(initialize)); // 第一次调用父类
}
objc_msgSend([JHPerson class],@selector(initialize)); // 调用子类,子类没实现,去找到父类调用,第二次调用父类
}
if (JHTeacher没有初始化) {
if (JHTeacher的父类Person没有初始化) { // 父类已经初始化了,不会进来
objc_msgSend([Person class], @selector(initialize));
}
objc_msgSend([JHTeacher class],@selector(initialize)); // 调用子类,子类没实现,去找到父类调用,第三次调用父类
}
- 聊聊block
- block的本质是一个OC对象
NSGloabalBlock --> NSBlock --> NSObject
- block的本质是一个OC对象
- block类型问题
- GloabalBlock – 没有访问auto变量 – 程序的数据区域
- NSStackBlock – 访问了auto变量 – 栈里面
- NSMallocBlock – NSStackBlock复制 – 堆里面
- NSMallockBlock复制,引用计数+1
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PeWg5rcG-1659199501480)(:/30c14379e1d04de58fd6b16fbc2fb6dd)]](/img/58/6f5e0b88deecc7f4a5fed41e3b3518.png)
- 变量捕获
- 局部变量会被捕获,全局变量不会被捕获
- static修饰的或者是__block修饰的,是指针传递
- 默认没有修饰,(即auto)是值传递
- 下划线_block修饰后的对象,是一个结构体
struct __Block_byref_age_0 { void *__isa; __Block_byref_age_0 *__forwarding; // 这个指针指向自己 int __flags; int __size; int age; };- forwarding为什么指向自己?如图,保证当从栈复制到堆时候,也能正确指向属性的指针地址
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dm6wIdsq-1659199501482)(:/0f99685a42514e41ad6c3e4a2797c715)]](/img/b7/0b8d51fd5a3b6e3fb26945c4b6f1b8.png)
- 循环引用
- ARC下
- 用__weak一般是最佳方案,因为安全,对象销毁指针指向nil
- 用_unsafe_unretain,不安全,对象销毁,指针地址不变
- __block 方案,个人取个名字叫
过河拆桥法,方便记忆。 - 首先__block修饰后,变为一个结构体。
- 结构体里面有
强指针指向实例对象,__block对实例对象产生了强引用
- 结构体里面有
实例对象调用block对象,实例对象对block产生了强引用
block再访问了__block对象的内容,block对__block产生了强引用,如图:
// 1. __block修饰的那部分:__block Person *person = [[Person alloc] init];
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong person; // 后面使用的person指针,都是这个指针
};
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iVMOYnk5-1659199501506)(:/1a2c46f22eaf41ba96794a004f9651d9)]](/img/36/ecdfdd69656d66881479ebdef6214e.png)
- 5. 现在我要过河拆桥,我调用block的时候,通过__block访问到了我想要访问的实例对象的属性(比如,这里我的目的就是访问person.age),那么我的目的已经完成了,所以,我不再需要让__block对象再去强引用实例对象了。
// 1. __block修饰的那部分:__block Person *person = [[Person alloc] init];
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong person = nil; // 在这里来过河拆桥,把它变为nil
};

- MRC
- 1.因为MRC不支持弱引用的情况,所以可以使用__unsafe_unretained
- 2.使用__block解决
因为__block不会对对象产生强引用
边栏推荐
- Third-party library-store
- Redux state management
- 快速傅里叶变换(FFT)
- 2. (1) Chained storage of stack, operation of chain stack (illustration, comment, code)
- 树状数组(单点修改区间查询和区间修改单点查询)
- 《白帽子说Web安全》思维导图
- leetcode 406. Queue Reconstruction by Height
- Dart入门
- 第十七章:回溯探求指定入口的马步遍历,贪心无回溯探求马步遍历,递归探求nxm棋盘带障碍马步遍历
- 拓扑排序的两种方法 --- dfs+Kahn
猜你喜欢

批量翻译软件免费【2022最新版】

Zotero | Zotero translator plugin update | Solve the problem that Baidu academic literature cannot be obtained

银河麒麟V10 sp1服务器安装英伟达显卡驱动

LeetCode刷题——摆动序列#376#Medium

服务器硬件及RAID配置实战

Automatic translation software - batch batch automatic translation software recommendation

postgresql源码学习(34)—— 事务日志⑩ - 全页写机制

关于求反三角函数的三角函数值

银河麒麟服务器v10 sp2安装oracle19c

安装和使用uView
随机推荐
Zabbix入门
使用powerDesigner反向工程生成Entity
Moment.js common methods
Install the gstreamer development dependency library to the project sysroot directory
关于求反三角函数的三角函数值
进程和计划任务管理
MySQL系列一:账号管理与引擎
Postgresql source code learning (33) - transaction log ⑨ - see the overall process of log writing from the insert record
银河麒麟高级服务器v10 sp1 手动加载Raid卡驱动
js原型详解
简单谈谈Feign
DirectExchange switch simple introduction demo
小实战项目之——吃货联盟订餐系统
芯塔电子斩获第十一届中国双创大赛芜湖赛区桂冠
搭建zabbix监控及邮件报警(超详细教学)
mysql的下载及安装使用
360推送-360推送工具-360批量推送工具
Zotero | Zotero translator plugin update | Solve the problem that Baidu academic literature cannot be obtained
TypeScript编译(tsconfig.json)
运行 npm 会弹出询问 “你要如何打开这个文件?“