美团优选面试题2及答案

美团优选面试题2及答案

美团优选面试题2

介绍值得说的项目

1.打印结果

1
2
3
4
5
6
7
- (void)someMethod {
int i = 10;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%d",i);
});
i = 20;
}

10

1
2
3
4
5
6
7
- (void)someMethod {
static int i = 10;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%d",i);
});
i = 20;
}

20

1
2
3
4
5
6
7
- (void)someMethod {
__block int i = 10;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%d",i);
});
i = 20;
}

20

1
2
3
4
5
6
7
- (void)someMethod {
int i = 10;
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"%d",i);
});
i = 20;
}

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_isKeykeyisKey顺序查找成员变量。找到了直接赋值。

没找到成员变量:调用 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指向

元类对象的 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
    里面存储的值是引用计数器减1

  • has_sidetable_rc
    引用计数器是否过大无法存储在 isa 中
    如果为1,那么引用计数会存储在一个叫 SideTable 的类的属性中

9.有做过卡顿检测吗?Runloop 检测卡顿怎么知道是哪个方法卡?方法卡了多长时间?

10.有做闪退监测吗?bugly 是怎么实现的?crash 率是多少?crash 率是怎么算出来的?

crash 率 = 崩溃次数 / 活跃设备数

11.两两交换链表中的结点

实现反转单向链表的函数。
如 1->2->3->4 反转后变成 2->1->4->3

LeetCode题目链接:两两交换链表中的节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var swapPairs = function(head) {
let dummyNode = new ListNode(0);
dummyNode.next = head;
let temp = dummyNode;
while (temp.next != null && temp.next.next != null) {
let head1 = temp.next;
let head2 = temp.next.next;
temp.next = head2;
head1.next = head2.next;
head2.next = head1;
temp = head1;
}
return dummyNode.next;
};

有什么想问我的?

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×