介绍值得说的项目
1.打印结果
1 | - (void)someMethod { |
10
1 | - (void)someMethod { |
20
1 | - (void)someMethod { |
20
1 | - (void)someMethod { |
10
2.+load 和 initialize 的区别?+load 的调用时机
1.调用方式
(1) load 是根据函数地址直接调用。
(2) initialize 是通过 objc_msgSend 调用。
2.调用时刻(什么时候会调用)
(1) load 是 runtime 加载类、分类的时候调用(只会调用一次)
(2) initialize 是类第一次接收到消息的时候调用,每一个类只会 initialize 一次(父类的 initialize 方法可能会被调用多次)。
3.App 启动 pre-main 做了什么事情
1.加载 dyld 到 App 进程(dyld 首先读取 mach-o Header 和 load commands,接着就知道了这个可执行文件依赖的动态库)
2.加载动态库(包括所依赖的所有动态库)
3.Rebase & Bind (使用了ASLR地址空间布局随机化和 Code Sign 来保证应用的安全,Rebase 修正内部指针指向,Bind 修正外部指针指向)
4.初始化 Objective-C Runtime (Runtime 维护了一张映射类名与类的全局表,当加载一个 dylib 时,把类的信息注册到这个全局表中,也会把 Category 中的方法注册到对应的类中)
5.Initializers 其他初始化代码
6.dyld 调用 main() 函数,main() 函数调用 UIApplicationMain(),程序启动。
4.KVO 的实现原理。
- 利用 Runtime API 动态生成一个子类,并且让 instance 对象的 isa 指向这个全新的子类
- 当修改 instance 对象的属性时,会调用 Foundation 的 _NSSetXXXValueAndNotify 函数
willChangeValueForKey:
父类原来的 setter
didChangeValueForKey:
内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)
5.KVC 的实现原理,是直接访问成员变量赋值吗?
按照 setKey:、_setKey: 顺序查找方法,找到方法调用。没找到继续。
查看 accessInstanceVariablesDirectly 方法的返回值。如果是 YES 继续。
按照_key
、_isKey
、key
、isKey
顺序查找成员变量。找到了直接赋值。
没找到成员变量:调用 setValue:forUndefinedKey: 并抛出异常NSUnknownKeyException
6.autoreleasepool 什么时候释放?
- 子线程使用了 autorelease 后,如果当前线程没有 AutorelesepoolPage 的话,代码执行顺序为 autorelease -> autoreleaseFast -> autoreleaseNoPage。在 autoreleaseNoPage 方法中,会创建一个 hotPage ,然后调用page->add(obj) 。也就是说即使这个线程没有 AutorelesepoolPage ,使用了 autorelease 对象时也会 new 一个 AutoreleasepoolPage 出来管理 autorelese 对象。
- 子线程的 autoreleasepool 是在线程销毁的时候释放的。
7.Category 添加一个属性系统做了什么?怎么添加属性?为什么不能添加成员变量。关联对象存在哪里?Category 里写了和类相同的方法会怎么样?
声明了 set 和 get 方法。
实现关联对象技术的核心对象有
AssociationsManager:关联对象存储在全局的统一的一个 AssociationsManager 中
AssociationsHashMap:key 为对象,value 为 ObjectAssociationMap
ObjectAssociationMap:key 为关联对象的 key,value 为 ObjcAssociation
ObjcAssociation:存储 policy 和 value
会先找到分类里的方法执行。
8.isa 指向,元类对象 isa 指向哪里?NSObject 的 isa 指向哪里?为什么要设计类、元类?isa 的结构
元类对象的 isa 指向基类的元类对象。NSObject 的 isa 指向它本身。
在 arm64 架构之前,isa 就是一个普通的指针,存储着 Class、Meta-Class 对象的内存地址
从 arm64 架构开始,对 isa 进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
nonpointer
0:代表普通的指针,存储着 Class、Meta-Class对象的内存地址
1:代表优化过,使用位域存储更多的信息has_assoc
是否有设置过关联对象,如果没有,释放时会更快has_cxx_dtor
是否有 C++ 的析构函数(.cxx_destruct),如果没有,释放时会更快shiftcls
存储着 Class、Meta-Class 对象的内存地址信息magic
用于在调试时分辨对象是否未完成初始化weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快deallocating
对象是否正在释放extra_rc
里面存储的值是引用计数器减1has_sidetable_rc
引用计数器是否过大无法存储在 isa 中
如果为1,那么引用计数会存储在一个叫 SideTable 的类的属性中
9.有做过卡顿检测吗?Runloop 检测卡顿怎么知道是哪个方法卡?方法卡了多长时间?
10.有做闪退监测吗?bugly 是怎么实现的?crash 率是多少?crash 率是怎么算出来的?
crash 率 = 崩溃次数 / 活跃设备数
11.两两交换链表中的结点
实现反转单向链表的函数。
如 1->2->3->4 反转后变成 2->1->4->3
LeetCode题目链接:两两交换链表中的节点
1 | var swapPairs = function(head) { |
有什么想问我的?