iOS积累用之IOS累之用之iOS面试专题

iOS 底层面试题

2021-05-28  本文已影响0人  木扬音

【面试-1】通过 Asssociate 方法关联的对象,需要在dealloc中释放

当对象释放时,系统会自动调用dealloc

dealloc释放步骤

由此可看出关联对象不需要我们手动移除,会在对象析构(dealloc)时释放

dealloc源码

【面试-2】类的方法 和 分类方法 重名,方法调用顺序

【面试-3】Runtime是什么?

【面试-4】方法的本质,sel是什么?IMP是什么?两者之间的关系又是什么?

【面试-5】能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量

【面试-6】 [self class]和[super class]的区别以及原理分析

image.png
[self class]
- (Class)class {
    return object_getClass(self);
}

👇
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;`
}

其底层是获取对象的isa,当前的对象是LGTeacher,那么isa就是同名的LGTeacher,所有[self class]打印的是LGTeacher

[super class]

通过clang的底层编译代码可知,当前的消息的接收者==self,而self==LGTeacher,所以[super class]进入class方法源码后,其中self是init后的实例对象,实例对象的isa指向本类,即消息接收者是LGTeacher本类

【面试-7】内存平移问题

LGPerson中有一个属性kc_name 和一个实例方法saySomething,通过下面代码,能否调用实例方法?

- (void)saySomething{
    NSLog(@"%s",__func__);
}

//下面这两种方式调用
//方式一
Class cls = [LGPerson class];
void  *kc = &cls; 
[(__bridge id)kc saySomething]; 

//方式二:常规调用
LGPerson *person = [LGPerson alloc];
 [person saySomething];

代码分析

所以,person是指向LGPerson类的结构,kc也是指向LGPerson类的结构,然后都是在LGPerson中的methodList中查找方法

image.png

修改:saySomething里面有属性 self.kc_name 的打印

代码如下所示

- (void)saySomething{
    NSLog(@"%s - %@",__func__,self.kc_name);
}

//下面这两种方式调用
//方式一
Class cls = [LGPerson class];
void  *kc = &cls; 
[(__bridge id)kc saySomething]; 

//方式二:常规调用
LGPerson *person = [LGPerson alloc];
 [person saySomething];

为什么会出现打印不一致的情况?

其中personkc_name 是由于 self指向person的内存结构,然后通过内存平移8字节,取出去kc_name,即self指针首地址平移8字节获得

image.png

我们可以通过下面这段代码打印栈的存储是否如上面所说

void *sp  = (void *)&self;
void *end = (void *)&person;
long count = (sp - end) / 0x8;
    
for (long i = 0; i<count; i++) {
    void *address = sp - 0x8 * i;
    if ( i == 1) {
        NSLog(@"%p : %s",address, *(char **)address);
    }else{
        NSLog(@"%p : %@",address, *(void **)address);
    }
}

运行结果如下


image.png

其中为什么class_getSuperclass是ViewController,因为objc_msgSendSuper2返回的是当前类,两个self,并不是同一个self,而是栈的指针不同,但是指向同一片内存空间

其中 personLGPerson的关系是 person是以LGPerson为模板的实例化对象,即alloc有一个指针地址,指向isa,isa指向LGPerson,它们之间关联是有一个isa指向

而kc也是指向LGPerson的关系,编译器会认为 kc也是LGPerson的一个实例化对象,即kc相当于isa,即首地址,指向LGPerson,具有和person一样的效果,简单来说,我们已经完全将编译器骗过了,即kc也有kc_name。由于person查找kc_name是通过内存平移8字节,所以kc也是通过内存平移8字节去查找kc_name

哪些东西在栈里 哪些在堆里
  • 是从小到大,即低地址->高地址
  • 是从大到小,即从高地址->低地址分配
    • 函数隐藏参数会从前往后一直压,即 从高地址->低地址 开始入栈
    • 结构体内部的成员是从低地址->高地址
  • 一般情况下,内存地址有如下规则
    • 0x60 开头表示在
    • 0x70 开头的地址表示在
    • 0x10 开头的地址表示在全局区域

【面试-8】 Runtime是如何实现weak的,为什么可以自动置nil

底层源码调用流程如下图所示


image.png
上一篇 下一篇

猜你喜欢

热点阅读