技术

iOS 高级面试题尝试解答

2018-12-19  本文已影响130人  Fisland_枫

[TOC]

iOS 基础题

  1. 分类和扩展有什么区别?可以分别用来做什么?分类有哪些局限性?分类的结构体里面有哪些成员?
    1. category:类别,分类
      1. 专门给类添加新的方法
      2. 不能给类添加成员属性,就算添加了,也无法获取到(其实可以通过runtime给分类添加属性)
      3. 分类中用@property定义变量,只会生成变量的getter,setter方法的声明,不能生成方法实现以及带下划线的成员变量
      4. 是运行期决议的
      5. 不能添加实例变量,和extension不同
      6. 原因:因为在运行期,对象的内存布局已经确定,如果添加实例变量会破坏类的内部布局,这对编译性语言是灾难性的。
    2. extension
      1. 可以说成是特殊的分类,也可以称为匿名分类
      2. 可给类添加成员变量,但是是私有变量
      3. 可以给类添加方法,也是私有方法
      4. 在编译器决议,是类的一部分,在编译器和头文件的@interface和实现文件里的@implement一起形成了一个完整的类
      5. 伴随类的产生而产生,也随着类的消息而消息
      6. extension一般用于隐藏类的私有消息,你必须有一个类的源码才能添加一个类的extension,类似NSString就无法添加
  2. 讲一下atomic的实现机制;为什么不能保证绝对的线程安全(最好可以结合场景来说)?
    1. atomic会加一个锁来保障线程安全,也就是保证了读写操作是安全的,并且引用计数会+1,来向调用者保证这个对象会一直存在
    2. 但是不能保证绝对的线程安全,而且还会造成额外的性能消耗,所以通常会使用nonatomic。例如,有一个线程a不断读取属性name(用atomic修饰)的值,同时有一个线程b不断修改name的值,那么线程读到的还是修改后的值,可见不是线程安全的。
    @property (atomic,copy) NSString *name;
    - (void)viewDidLoad {
        [super viewDidLoad];

        self.name = @"aaa";
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 1000; i++) {
                NSLog(@"self.name ===== %@",self.name);
            }
        });

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            self.name = @"bbb";
        });

    }   
    /*输出
    2018-12-18 16:08:05.269780+0800 testcopy[3154:2072066] index = 10,self.name ===== aaa
    2018-12-18 16:08:05.269937+0800 testcopy[3154:2072066] index = 11,self.name ===== bbb
    */
  1. 被weak修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable么?里面的结构可以画出来么?
    1. 释放时,会调用clearDeallocating函数,该函数动作如下
      1. 从weak表中获取废弃对象的地址作为键值的记录
      2. 将包含在记录中的所有附有weak修复符变量的地址,赋值为nil
      3. 将weak表中该记录删除
      4. 从引用计数表中删除废弃对象的地址为键值的记录
    2. Side这个结构体主要用于管理对象的引用计数和weak表,在NSObject.mm中声明起数据结构
struct SideTable {
    spinlock_t slock;//保证原子操作的自旋锁
    RefcountMap refcnts;//引用计数的hash表
    weak_table_t weak_table;//weak引用全局的hash表

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
  1. 关联对象有什么应用,系统如何管理关联对象?其被释放的时候需要手动将所有的关联对象的指针置空么?
    1. 因为在分类category@property不会自动生成实例变量和存取方法,这时候就可以使用关联对象可以为已经存在的类中添加属性
    2. 系统通过管理一个全局哈希表,通过对象指针地址和传递的固定参数地址来获取关联对象。根据setter传入的参数协议,来管理对象的生命周期。
      1. 关联对象其实就是 ObjcAssociation 对象
      2. 关联对象由 AssociationsManager 管理并在 AssociationsHashMap 存储
      3. 对象的指针以及其对应 ObjectAssociationMap 以键值对的形式存储在 AssociationsHashMap 中
      4. ObjectAssociationMap 则是用于存储关联对象的数据结构
      5. 每一个对象都有一个标记位 has_assoc 指示对象是否含有关联对象 (isa)
    3. 不需要,传入的objc_AssociationPolicy是内存管理的枚举类型,无论是哪个协议都不需要手动自动,ps:unsafe_unretain一般认为外部有对象控制,所以对象不用处理
#import "DKObject+Category.h"
#import <objc/runtime.h>

@implementation DKObject (Category)

- (NSString *)categoryProperty {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setCategoryProperty:(NSString *)categoryProperty {
    objc_setAssociatedObject(self, @selector(categoryProperty), categoryProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//objc_AssociationPolicy 协议类型
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
    OBJC_ASSOCIATION_RETAIN = 01401, 
    OBJC_ASSOCIATION_COPY = 01403
};
@end
  1. KVO的底层实现?如何取消系统默认的KVO并手动触发(给KVO的触发设定条件:改变的值符合某个条件时再触发KVO)?
    1. 当观察某个对象A时,KVO机制动态创建一个对象A当前类的子类,并为这个新的子类重写了观察属性keypath的setter方法。重写的setter方法会负责在调用原setter方法之前和之后,通知所有的观察对象:值的改变
    2. Apple使用isa混写(isa-swizzling)来实现KVO。当观察对象A时候,KVO机制动态创建一个新的名为:NSKVONotifying_A的新类,该类集成自对象A的本类,NSKVONotifying_A的重写观察属性的setter方法,并在setter调用前后,通知所有观察属性值得更改情况
@property (nonatomic,copy) NSString *name;

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    if ([key isEqualToString:@"name"]) {
        return NO;
    }
    else{
        return [super automaticallyNotifiesObserversForKey:key];
    }
}

- (void)setName:(NSString *)name{
    if (_name != name) {
        [self willChangeValueForKey:@"name"];
        _name = name;
        [self didChangeValueForKey:@"name"];
    }
}
  1. Autoreleasepool所使用的数据结构是什么?AutoreleasePoolPage结构体了解么?
    1. 双链表结构,将main.m转换为cpp代码会发现autoreleasepool有这样的代码,一个构造函数,一个析构函数,再到runtime源码中看到AutoreleasePoolPage,发现调用了Autorelease对象都是通过调用AutoreleasePoolPage对象来管理的
    2. AutoreleasePoolPage有以下两点
      1. 每个AutoReleasePoolPage对象占用4096个字节的内存,出了用来存放它内部的成员变量外,剩下的空间用来存放Autorelease对象的地址。
      2. 所有的AutoreleasePoolPage对象都是通过双向链表的形式连接在一起
//main.cpp
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};
//runtime 源代码 NSObject.mm
void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

class AutoreleasePoolPage 
{
    ...
    static size_t const SIZE = PAGE_MAX_SIZE;
    ...
    magic_t const magic;                   // 16字节
    id *next;                              // 8字节
    pthread_t const thread;                // 8字节
    AutoreleasePoolPage * const parent;    // 8字节 
    AutoreleasePoolPage *child;            // 8字节
    uint32_t const depth;                  // 4字节
    uint32_t hiwat;                        // 4字节
    ...
}
#define PAGE_MAX_SIZE           PAGE_SIZE
#define PAGE_SIZE              I386_PGBYTES
#define I386_PGBYTES            4096
  1. 讲一下对象,类对象,元类,跟元类结构体的组成以及他们是如何相关联的?为什么对象方法没有保存的对象结构体里,而是保存在类对象的结构体里?
  2. class_ro_t 和 class_rw_t 的区别?
    1. class_ro_t,objc类中的属性、方法还有遵循的协议等信息都保存在class_rw_t中
    2. class_rw_t,存储了当前类在编译期就已经确定的属性、方法和遵循的协议

struct class_rw_t {  
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
};
    
struct class_ro_t {  
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    uint32_t reserved;

    const uint8_t * ivarLayout;

    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
}
  1. iOS 中内省的几个方法?class方法和objc_getClass方法有什么区别?

    1. 内省方法
      1. isKindOfClass:Class 检查对象是否是那个类或者其继承类实例化的对象
      2. isMemberOfClass:Class 检查对象是否是那个类但不包括继承类而实例化的对象
      3. respondToSelector:selector,检查对象是否包含这个方法
      4. conformsToProtocol:protocol 检查对象是否符合协议,是否实现协议中所有的必选方法
    2. 区别
  2. 在运行时创建类的方法objc_allocateClassPair的方法名尾部为什么是pair(成对的意思)?

  3. 一个int变量被__block修饰与否的区别?

    1. 如果在block内部修改int变量的值,需要加上__block,不然会报错
  4. 为什么在block外部使用__weak修饰的同时需要在内部使用__strong修饰?

    1. 使用__weak只要是为了避免循环引用,导致内存无法释放
    2. 在内部使用__strong这个对象是为了防止这个对象在别处被释放,如果调用了改变量的block还没执行结束,那么系统就会保等待block执行完后再释放,对该变量在block中的使用起到保护作用
    3. 当block执行结束后自动释放掉
  5. RunLoop的作用是什么?它的内部工作机制了解么?(最好结合线程和内存管理来说)

    1. iOS RunLoop知识整理
  6. 哪些场景可以触发离屏渲染?(知道多少说多少)

    1. 高耗能的绘制动作,类似圆角、阴影(mask, shadow, group opacity, edge antialiasing)

iOS 实战题

  1. AppDelegate如何瘦身?
  2. 反射是什么?可以举出几个应用场景么?(知道多少说多少)
  3. 有哪些场景是NSOperation比GCD更容易实现的?(或是NSOperation优于GCD的几点,知道多少说多少)
  4. App 启动优化策略?最好结合启动流程来说(main()函数的执行前后都分别说一下,知道多少说多少)
  5. App 无痕埋点的思路了解么?你认为理想的无痕埋点系统应该具备哪些特点?(知道多少说多少)
  6. 你知道有哪些情况会导致app崩溃,分别可以用什么方法拦截并化解?(知道多少说多少)
  7. 你知道有哪些情况会导致app卡顿,分别可以用什么方法来避免?(知道多少说多少)

网络题

  1. App 网络层有哪些优化策略?
  2. TCP为什么要三次握手,四次挥手?
  3. 对称加密和非对称加密的区别?分别有哪些算法的实现?
  4. HTTPS的握手流程?为什么密钥的传递需要使用非对称加密?双向认证了解么?
  5. HTTPS是如何实现验证身份和验证完整性的?
  6. 如何用Charles抓HTTPS的包?其中原理和流程是什么?
  7. 什么是中间人攻击?如何避免?

计算机系统题

  1. 了解编译的过程么?分为哪几个步骤?
  2. 静态链接了解么?静态库和动态库的区别?
  3. 内存的几大区域,各自的职能分别是什么?
  4. static和const有什么区别?
  5. 了解内联函数么?
  6. 什么时候会出现死锁?如何避免?
  7. 说一说你对线程安全的理解?
  8. 列举你知道的线程同步策略?
  9. 有哪几种锁?各自的原理?它们之间的区别是什么?最好可以结合使用场景来说

设计模式题

  1. 除了单例,观察者设计模式以外,还知道哪些设计模式?分别介绍一下
  2. 最喜欢哪个设计模式?为什么?
  3. iOS SDK 里面有哪些设计模式的实践?
  4. **设计模式是为了解决什么问题的?
  5. **设计模式的成员构成以及工作机制是什么?
  6. **设计模式的优缺点是什么?

架构 & 设计题

  1. MVC和MVVM的区别?MVVM和MVP的区别?
  2. 面向对象的几个设计原则了解么?最好可以结合场景来说。
  3. 可以说几个重构的技巧么?你觉得重构适合什么时候来做?
  4. 你觉得框架和设计模式的区别是什么?
  5. 看过哪些第三方框架的源码,它们是怎么设计的?设计好的地方在哪里,不好的地方在哪里,如何改进?(这道题的后三个问题的难度已经很高了,如果不是太N的公司不建议深究)

数据结构&算法题

  1. 链表和数组的区别是什么?插入和查询的时间复杂度分别是多少?
  2. 哈希表是如何实现的?如何解决地址冲突?
  3. 排序题:冒泡排序,选择排序,插入排序,快速排序(二路,三路)能写出那些?
  4. 链表题:如何检测链表中是否有环?如何删除链表中等于某个值的所有节点?
  5. 数组题:如何在有序数组中找出和等于给定值的两个元素?如何合并两个有序的数组之后保持有序?
  6. 二叉树题:如何反转二叉树?如何验证两个二叉树是完全相等的?

资料来源
几个iOS基础题目总结
出一套 iOS 高级面试题
关联对象 AssociatedObject 完全解析

上一篇下一篇

猜你喜欢

热点阅读