iOS面试题分享:快手

建了个裙,群号:812157648,有兴趣的可以加一下,大家一起相互学习。

一.算法

两个数n、m 如果是n= 2 m=5,用递归实现2 3 4 5相加等于14;


    public static int sum(int n1, int n2) {
        if(n1 == n2) {
            return n1;
        }
         
        if(n1 >  n2) {
            int temp = n1;
            n1 = n2;
            n2 = temp;
        }
         
        return sum(n1, n2-1) + n2;
    }
    

二.weak和assign的区别

1、区别

  • 修饰变量类型的区别
    weak 只可以修饰对象。如果修饰基本数据类型,编译器会报错-“Property with ‘weak’ attribute must be of object type”。
    assign 可修饰对象,和基本数据类型。当需要修饰对象类型时,MRC时代使用unsafe_unretained。当然,unsafe_unretained也可能产生野指针,所以它名字是"unsafe_”。

  • 是否产生野指针的区别
    weak 不会产生野指针问题。因为weak修饰的对象释放后(引用计数器值为0),指针会自动被置nil,之后再向该对象发消息也不会崩溃。 weak是安全的。
    assign 如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会自动被置空,此时向对象发消息会崩溃。

2、相似

都可以修饰对象类型,但是assign修饰对象会存在问题。

3、总结

assign 适用于基本数据类型如int,float,struct等值类型,不适用于引用类型。因为值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们自己手动管理内存或通过ARC管理。
weak 适用于delegate和block等引用类型,不会导致野指针问题,也不会循环引用,非常安全。

三.weak原理(很细致,具体到如何查找的)

1、调用objc_release

2、因为对象的引用计数为0,所以执行dealloc

3、在dealloc中,调用了_objc_rootDealloc函数

4、在_objc_rootDealloc中,调用了object_dispose函数

5、调用objc_destructInstance

6、最后调用objc_clear_deallocating。

详情

四.autoreleasePool 的结构和自动释放池中的对象存储过程,autoreleasePool的结构。(比较细致的问了一遍过程)

关键字:结构体 parent和child 双向链表

详情

五.objc_msgSend()经历的过程,具体到cache_t结构和具体hashmap的查找方法。动态解析和消息转发要说具体的方法名称。

objc_msgSend汇编部分仅仅完成很少的缓存查找功能,如果找不到就会调用C方法去对象的方法二维数组中找,找不到再查父类的缓存(这也是汇编实现的)和父类的方法数组,一直找到根类,如果此过程中找到对应的方法则调用并添加缓存,如果没有找到,则表明该继承体系都没有直接实现该方法,这时runtime会调用对象的方法决议去尝试解决。如果不行则由CoreFoundation框架提供的forwarding来转发到其他对象处理,若还不能处理则抛出异常。

详情

六.block的捕获机制,block类型的区分,__block做了什么,__block修饰对象类型和基本数据类型的区别。

全局堆栈带__block的自动变量 和 静态变量 就是直接地址访问。所以在Block里面可以直接改变变量的值。
剩下的静态全局变量,全局变量,函数参数,也是可以在直接在Block中改变变量值的,但是他们并没有变成Block结构体__main_block_impl_0的成员变量,因为他们的作用域大,所以可以直接更改他们的值。

详情上

详情下

七.load()和initialize()区别

调用方式

1、load是根据函数地址直接调用

2、initialize是通过objc_msgSend调用

调用时刻

1、load是runtime加载类、分类的时候调用(只会调用一次)

2、initialize是类第一次接收到消息的时候调用, 每一个类只会initialize一次(如果子类没有实现initialize方法, 会调用父类的initialize方法, 所以父类的initialize方法可能会调用多次)

load和initializee的调用顺序

1、load:父子分

先调用类的load, 在调用分类的load

先编译的类, 优先调用load, 调用子类的load之前, 会先调用父类的load

先编译的分类, 优先调用load

image

2、initialize 分子父
先初始化分类, 后初始化子类
通过消息机制调用, 当子类没有initialize方法时, 会调用父类的initialize方法, 所以父类的initialize方法会调用多次

八.图层方法两倍形变后,frame和bouns的变化,相对位置,绝对位置。

阅读原文

推荐阅读更多精彩内容