美文网首页
iOS开发面试整理

iOS开发面试整理

作者: 昵称已经被使用换个别的 | 来源:发表于2022-02-19 22:13 被阅读0次

    一、Runtime

    1、id、instance
    id

    使用id修饰的对象是动态类型,只是简单的声明了指向对象的指针。
    编译时不做类型检查,可以发送任何信息给id类型的对象

    instanceType

    表示某个方法返回未知类型的OC对象
    非关联类型的方法返回所在类的类型

    instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象
    instancetype只能作为返回值,不能像id一样作为参数

    2、isa

    Apple文档:

    Every object is connected to the run-time system through itsisa instance variable, inherited from the NSObject class.isa identifies the object's class; it points to a structurethat's compiled from the class definition. Through isa, anobject can find whatever information it needs at run timesuch asits place in the inheritance hierarchy, the size and structure ofits instance variables, and the location of the methodimplementations it can perform in response to messages.

    isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。

    每一个对象本质上都是一个类的实例。其中类定义了成员变量和成员方法的列表。对象通过对象的isa指针指向类。
    每一个类本质上都是一个对象,类其实是元类(meteClass)的实例。元类定义了类方法的列表。类通过类的isa指针指向元类。
    所有的元类最终继承一个根元类,根元类isa指针指向本身,形成一个封闭的内循环。

    3、消息发送机制
    4、Method Swizzing(方法交换)

    参考文章黑魔法

    5、Category、Extension
    • Category是运行时决定生效的,Extension是编译时就决定生效的
    • Category可以为系统类添加分类,Extension不能
    • Category是有声明和实现,Extension直接写在宿主.m文件,只有声明
    • Category只能扩充方法,不能扩充成员变量和属性
    • 如果Category声明了一个属性,那么Category只会生成这个属性的set,get方法的声明,也就不是会实现。我们知道在一个类中用@property声明属性,编译器会自动帮我们生成成员变量和setter/getter,但分类的指针结构体中,根本没有属性列表。所以在分类中用@property声明属性,既无法生成成员变量也无法生成setter/getter。
      因此结论是:我们可以用@property声明属性,编译和运行都会通过,只要不使用程序也不会崩溃。但如果调用了_成员变量和setter/getter方法,报错就在所难免了。
    6、归档接档(NSCoding)
    7、KVC、KVO

    二、Runloop

    1、什么是Runloop
    2、Runloop Mode
    3、Runloop Source

    事件源,即事件的处理。CFRunLoopSource 是循环系统的事件输入源,输入源通常会产生异步事件,然后随着 RunLoop 生命周期的进行不断执行回调。Source 有两个版本:Source0 和 Source1。Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程。

    4、Runloop Observer

    观察者,即 RunLoop 生命周期的回调。每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。

    5、Runloop运行逻辑
    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry         = (1UL << 0), // 即将进入 RunLoop
        kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
        kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
        kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠被唤醒
        kCFRunLoopExit          = (1UL << 7), // 即将退出 RunLoop
        kCFRunLoopAllActivities = 0x0FFFFFFFU // 任意上述活动
    };
    
    6、如何实现常驻线程
    - (void)run{
        //只要往RunLoop中添加了  timer、source或者observer就会继续执行,一个Run Loop通常必须包含一个输入源或者定时器来监听事件,如果一个都没有,Run Loop启动后立即退出。
        
        @autoreleasepool {
        
        //1、添加一个input source
            [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
        //2、添加一个定时器
        //    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
        //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        //    [[NSRunLoop currentRunLoop] run];
        }
    }
    

    三、Block

    1、Block原理
    2、堆Block、栈Block、全局Block
    3、Block变量捕获
    4、_ _block _ _weak
    • _ _block不管是ARC还是MRC下都能使用,可以修饰对象,还可以修饰基本数据类型。
    • _ _weak只能在ARC下使用,只能修饰对象,不能修饰基本数据类型。
    • _ _block对象可以在block中被重新赋值, _ _weak不可以。
    • _ _ block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用; _ _weak只能在ARC下使用,可以避免循环引用。
    5、Weakself

    四、内存管理

    1、引用计数
    2、MRC和ARC
    3、weak、assign、strong与copy
    weak

    只可以修饰对象。如果修饰基本数据类型,编译器会报错。weak 不会产生野指针问题。因为weak修饰的对象释放后(引用计数器值为0),指针会自动被置nil,之后再向该对象发消息也不会崩溃。 因此,weak适用于delegate和block等引用类型,不会导致野指针问题,也不会循环引用。

    assign

    可修饰对象,和基本数据类型。当需要修饰对象类型时,MRC下使用unsafe_unretained。assign 如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会自动被置空,此时向对象发消息会崩溃。因此assign 适用于基本数据类型如int,float,struct等值类型,不适用于引用类型。因为值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们自己手动管理内存或通过ARC管理。

    strong与copy

    两者修饰的对象在引用时引用计数都会加1,常见题NSString在声明时用strong还是copy修饰(参考文章https://www.jianshu.com/p/b3873ac9259b

    4、深copy与浅copy

    引申一个小知识,地址有两种情况:

    • 指针的内存地址
    • 指针指向的对象的内存地址

    所以打印内存地址的正确姿势如下:

    NSString *string = @"Jianshu";
    NSLog(@"指针所指向对象的内存地址:%p",string);
    NSLog(@"指针自己的内存地址:%p",&string);
    

    深拷贝就是拷贝出和原来仅仅是值一样,但是内存地址完全不一样的新的对象,创建后和原对象没有任何关系。深拷贝是真正意义上的拷贝,是创建一个新对象。copy属性表示两个对象内容相同,新的对象retain为1,与原对象的引用计数无关,原对象没有改变。copy减少对象对上下文的依赖。

    浅拷贝就是拷贝指向原来对象的指针,使原对象的引用计数+1,可以理解为创建了一个指向原对象的新指针而已,并没有创建一个全新的对象。浅拷贝类似retain,引用计数对象+1,创建一个指针。

    5、atomic(原子性)和noatomic(非原子性)

    Apple文档



    原子操作是不可分割的操作,在原子操作执行完毕之前,其不会被任何其它任务或事件中断。
    atomic与nonatomicd的主要区别就是系统自动生成的getter/setter方法不一样

    • atomic系统自动生成的getter/setter方法会进行加锁操作,不同线程上的操作都将依次顺序执行
    • nonatomic系统自动生成的getter/setter方法不会进行加锁操作,不同线程上的操作可以同时执行,可能导致无法预料的结果。
      假设有一个 atomic 的属性 "name",如果线程 A 调[self setName:@"A"],线程 B 调[self setName:@"B"],线程 C 调[self name],那么所有这些不同线程上的操作都将依次顺序执行,也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。因此,属性 name 是读/写安全的。

    但是,如果有另一个线程 D 同时在调[name release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。也就是说,这个属性只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。

    如果 name 属性是 nonatomic 的,那么上面例子里的所有线程 A、B、C、D 都可以同时执行,可能导致无法预料的结果。如果是 atomic 的,那么 A、B、C 会串行,而 D 还是并行的。

    6、内存泄漏
    7、循环引用

    五、多线程

    1、GCD
    2、NSOperation
    3、NSThread

    六、iOS网络

    1、TCP三次握手

    三次握手主要是
    为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误,假设这是一个早已失效的报文段。但 serverl收到此失效的连接请求报文段后,就误认为是 client再次发出的一个新的连接请求。于是就向 client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要 server发出确认,新的连接就建立了。由于现在 client并没有发出建立连接的请求,因此不会理睬 server的确认,也不会向 server发送数据,但 Server却以为新的运输连接已经建立,并直等待cent发来数据,这样, server的很多资源就白白浪费掉了。

    2、TCP四次挥手

    四次挥手主要是因为TCP是全双工通信的,在接收到客户端的关闭请求时,还可能在向客户端发送着数据,因此不能再回应关闭链接的请求时,同时发送关闭链接的请求。

    3、TCP与UDP的应用场景与区别
    4、HTTP与HTTPS的区别
    5、TLS的加密过程
    6、TLS的握手过程
    7、TLS握手时的随机数有什么作用

    七、性能优化

    1、界面优化原理:视图加载、项目卡顿、异步渲染等
    2、启动优化原理:App首次启动、冷/热启动原理分析
    3、耗电优化原理

    相关文章

      网友评论

          本文标题:iOS开发面试整理

          本文链接:https://www.haomeiwen.com/subject/axnltrtx.html