美文网首页iOS-面试iOS CodingiOS面试题+基础知识
[iOS]底层原理五 : (面试题目整理)

[iOS]底层原理五 : (面试题目整理)

作者: iLees | 来源:发表于2019-10-10 22:08 被阅读0次

    底层原理一: (OC 本质、KVC、KVO、Category、Block)
    底层原理二: (Runtime、Runloop)
    底层原理三 : (多线程、内存管理)
    底层原理四 : (性能优化、架构)
    底层原理五 : (面试题目整理)

    1. 一个OC对象占用多少内存

    系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
    但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
    
    

    2. 对象的isa指针指向哪里?

    instance对象的isa指向class对象
    class对象的isa指向meta-class对象
    meta-class对象的isa指向基类的meta-class对象
    
    

    3.OC的类信息存放在哪里?

    对象方法、属性、成员变量、协议信息,存放在class对象中
    类方法,存放在meta-class对象中
    成员变量的具体值,存放在instance对象
    
    

    4.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

    - 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
    - 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
        willChangeValueForKey:
        父类原来的setter
        didChangeValueForKey:
    - 内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)
    
    

    5.如何手动触发KVO?

    手动调用willChangeValueForKey:和didChangeValueForKey:
    //
    - (void)viewDidLoad {
    [super viewDidLoad];
    
        Person *person = [[Person alloc]init];;
        [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
        [p willChangeValueForKey:@"name"];
        [p didChangeValueForKey:@"name"];
    }
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        NSLog(@"被观测对象:%@, 被观测的属性:%@, 值的改变: %@\n, 携带信息:%@", object, keyPath, change, context);
    }
    
    

    6.直接修改成员变量会触发KVO么?

    不会触发KVO
    
    

    7.通过KVC修改属性会触发KVO么?

    会触发KVO
    KVC在赋值时候,内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:) 发送通知
    
    

    8.KVC的赋值和取值过程是怎样的?原理是什么?

    KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性
    调用 setValue:forKey:
    setKey,_setKey  ->找到了则进行赋值,未找到调用 accessInstanceVarlableDirctly 是否运行 修改值,返回YES
    调用_key, _isKey, key, isKey 进行赋值
    
    

    9.Category的使用场合是什么?

    - 在不修改原有类代码的情况下,为类添对象方法或者类方法
    - 或者为类关联新的属性
    - 分解庞大的类文件
    
    使用场合:
    - 添加实例方法
    - 添加类方法
    - 添加协议
    - 添加属性
    - 关联成员变量
    
    

    10.Category的实现原理

    Category编译之后的底层结构是struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息
    在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
    
    

    11.Category和Class Extension的区别是什么?

    Class Extension在编译的时候,它的数据就已经包含在类信息中
    Category是在运行时,才会将数据合并到类信息中
    
    

    12.Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?

    - 有load方法
    - load方法在runtime加载类、分类的时候调用
    - load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用
    
    

    13. initialize方法如何调用,以及调用时机

    - 当类第一次收到消息的时候会调用类的initialize方法
    - 是通过 runtime 的消息机制 objc_msgSend(obj,@selector()) 进行调用的
    - 优先调用分类的 initialize, 如果没有分类会调用 子类的,如果子类未实现则调用 父类的
    
    

    13. load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?

    - load 是类加载到内存时候调用, 优先父类->子类->分类
    - initialize 是类第一次收到消息时候调用,优先分类->子类->父类
    - 同级别和编译顺序有关系
    - load 方法是在 main 函数之前调用的
    
    

    14. Category能否添加成员变量?如果可以,如何给Category添加成员变量?

    不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果
    Category是发生在运行时,编译完毕,类的内存布局已经确定,无法添加成员变量(Category的底层数据结构也没有成员变量的结构)
    可以通过 runtime 动态的关联属性
    
    

    15. block的原理是怎样的?本质是什么?

    block 本质其实是OC对象
    block 内部封装了函数调用以及调用环境
    
    

    16. __block的作用是什么?有什么使用注意点?

    如果需要在 block 内部修改外部的 局部变量的值,就需要使用__block 修饰(全局变量和静态变量不需要加__block 可以修改)
    
    __block 修饰以后,局部变量的数据结构就会发生改变,底层会变成一个结构体的对象,结构内部会声明 一个 __block修饰变量的成员, 并且将 __block修饰变量的地址保存到堆内存中. 后面如果修改 这个变量的值,可以通过 isa 指针找到这个结构体,进来修改 这个变量的值;
    
    可以在 block 内部修改 变量的值
    
    

    17. block的属性修饰词为什么是copy?使用block有哪些使用注意?

    block 一旦没有进行copy操作,就不会在堆上
    使用注意:循环引用问题 (外部使用__weak 解决)
    
    

    17. block在修改NSMutableArray,需不需要添加__block?

    如果是操作 NSMutableArray 对象不需要,因为 block 内部拷贝了 NSMutableArray对象的内存地址,实际是通过内存地址操作的
    如果 NSMutableArray 对象要重新赋值,就需要加__block
    
    

    18. Block 内部为什么不能修改局部变量,需要加__block

    通过查看Block 源码,可以发现, block 内部如果单纯使用 外部变量, 会在 block 内部创建同样的一个变量,并且将 外部变量的值引用过来..(只是将外部变量值拷贝到 block 内部), 内部这个变量和外部 实际已经没关系了
    
    从另一方面分析,block 本质也是一个 函数指针, 外部的变量也是一个局部变量,很有可能 block 在使用这个变量时候,外部变量已经释放了,会造成错误
    
    加了__block 以后, 会将外部变量的内存拷贝到堆中, 内存由 block 去管理.
    
    

    19.讲一下 OC 的消息机制

    OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
    objc_msgSend底层有3大阶段
    消息发送(当前类、父类中查找)、动态方法解析、消息转发
    
    
    19.1 消息发送流程
    当我们的一个 receiver(实例对象)收到消息的时候, 会通过 isa 指针找到 他的类对象, 然后在类对象方法列表中查找 对应的方法实现,如果 未找到,则会通过 superClass 指针找到其父类的类对象, 找到则返回,未找打则会一级一级往上查到,最终到NSObject 对象, 如果还是未找到就会进行动态方法解析
    
    类方法调用同上,只不过 isa 指针找到元类对象;
    
    
    19.1 动态方法解析机制
    当我们发送消息未找到方法实现,就会进入第二步,动态方法解析: 代码实现如下
    
    //  动态方法绑定- 实例法法调用
    + (BOOL)resolveInstanceMethod:(SEL)sel{
        if (sel == @selector(run)) {
            Method method = class_getInstanceMethod(self, @selector(test));
            class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    // 类方法调用
    +(BOOL) resolveClassMethod:(SEL)sel....
    
    

    20.消息转发机制流程

    未找到动态方法绑定,就会进行消息转发阶段
    
    // 快速消息转发- 指定消息处理对象
    - (id)forwardingTargetForSelector:(SEL)aSelector{
        if (aSelector == @selector(run)) {
            return [Student new];
        }
        return  [super forwardingTargetForSelector:aSelector];
    } 
    
    // 标准消息转发-消息签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        if(aSelector == @selector(run))
        {
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    - (void)forwardInvocation:(NSInvocation *)anInvocation{
       //内部逻辑自己处理 
    }
    
    

    21.什么是Runtime?平时项目中有用过么?

    Objective-C runtime是一个`运行时`库,它为Objective-C语言的动态特性提供支持,我们所写的OC代码在运行时都转成了runtime相关的代码,类转换成C语言对应的结构体,方法转化为C语言对应的函数,发消息转成了C语言对应的函数调用。通过了解runtime以及源码,可以更加深入的了解OC其特性和原理
    
    OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
    OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
    平时编写的OC代码,底层都是转换成了Runtime API进行调用
    
    

    22.runtime具体应用

    利用关联对象(AssociatedObject)给分类添加属性
    遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
    交换方法实现(交换系统的方法)
    利用消息转发机制解决方法找不到的异常问题
    
    

    23.打印结果分别是什么?

    1.png
    [self class] 和 [super class] 都是给当前类返送消息,spuer 表示在父类中查找
    [self superClass]  和 [super superclass] 也是也当前类发消息,返回父类
    第一个打印:
    MJStudent / MJStudent/ MJerson / MJPerson
    
    isKindOfClass 表示对象是否为当前类或者子类的 类型
    isMemberOfClass 表示是否为当前类的的类型
    isMemberOfClass 分为- 对象方法 和+ 类方法2中
    - (bool)isMemberOfClass; 比较的是类对象
    + (bool)isMemberOfClass; 比较的是元类
    第二个打印:
    1 ,0, 0, 0
    
    

    24.以下代码能不能执行成功?如果可以,打印结果是什么?

    2.png
    打印结果: <ViewController: 0x7f9396c16300>
    
    

    25.讲讲 RunLoop,项目中有用到吗?

    runloop运行循环,保证程序一直运行,主线程默认开启
    用于处理线程上的各种事件,定时器等
    可以提高程序性能,节约CPU资源,有事情做就做,没事情做就让线程休眠
    
    应用范畴:
    定时器,事件响应,手势识别,界面刷新,以及autoreleasePool 等等
    

    26.runloop内部实现逻辑?

    3.png
    实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。
    
    

    27.runloop和线程的关系?

    每条线程都有唯一的一个与之对应的RunLoop对象
    RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
    线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
    RunLoop会在线程结束时销毁
    主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
    
    

    28.timer 与 runloop 的关系?

    timer 定时器,是基于 runloop 来实现的, runloop 在运行循环当中,监听到了定制器 就会执行;所以 timer 需要添加到 runloop 中去, 注意子线程的 runloop 默认是不开启的,如果在子线程执行 timer 需要手动开启 runloop
    
    

    29.程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?

    将 timer 对象添加到 runloop 中,并修改 runloop 的运行 mode
    
     NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:nil];
     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    
    

    30.runloop 是怎么响应用户操作的, 具体流程是什么样的?

    不明白问题想问什么?
    
    

    31.说说runLoop的几种状态

    添加Observer监听RunLoop的所有状态

    4.png

    32.runloop的mode作用是什么?

    runloop 只能在一种 mode 下运行, 做不同的事情,runloop 会切换到对应的 model 下来执行,默认是  kCFRunLoopDefaultMode 如果视图滑动再回切换到  UITrackingRunLoopMode,如果需要在多种 mode 下运行则需要手动设置 kCFRunLoopCommonModes;
    
    1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
    2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
    4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
    5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode 
    
    

    33.你理解的多线程?

    同一时间,CPU 只能处理理一条线程, 只有一条线程在⼯工作 多线程并发执行,其实是 CPU 快速的在多条线程之间调度(切换) 如果 CPU 调度线程的时间⾜足够快, 就造成了多线程并发执⾏的假象
    
    优势
    充分发挥多核处理器的优势,将不同线程任务分配给不同的处理器,真正进入“⾏行 计算”状态
    弊端 
    新线程会消耗内存控件和cpu时间,线程太多会降低系统行性能。
    
    

    34.iOS的多线程方案有哪几种?你更倾向于哪一种?

    5.png
    倾向于GCD ,简单灵活,使用方便
    
    

    35.你在项目中用过 GCD 吗?

    使用过
    
    GCD中有2个用来执行任务的函数
    用同步的方式执行任务
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    queue:队列
    block:任务
    
    用异步的方式执行任务
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    
    

    36.GCD 的队列类型

    GCD的队列可以分为2大类型
    并发队列(Concurrent Dispatch Queue)
    可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    并发功能只有在异步(dispatch_async)函数下才有效
    
    串行队列(Serial Dispatch Queue)
    让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
    
    

    37.说一下 OperationQueue 和 GCD 的区别,以及各自的优势

    1> GCD是纯C语⾔言的API,NSOperationQueue是基于GCD的OC版本封装
    2> GCD只⽀支持FIFO的队列列,NSOperationQueue可以很⽅方便便地调整执⾏行行顺 序、设 置最⼤大并发数量量
    3> NSOperationQueue可以在轻松在Operation间设置依赖关系,⽽而GCD 需要写很 多的代码才能实现
    4> NSOperationQueue⽀支持KVO,可以监测operation是否正在执⾏行行 (isExecuted)、 是否结束(isFinished),是否取消(isCanceld)
    5> GCD的执⾏行行速度⽐比NSOperationQueue快 任务之间不不太互相依赖:GCD 任务之间 有依赖\或者要监听任务的执⾏行行情况:NSOperationQueue
    
    

    38.线程安全的处理手段有哪些?

    1.加锁
    2.同步执行
    
    

    39.OC你了解的锁有哪些?在你回答基础上进行二次提问;

    os_unfair_lock  ios10 开始
    OSSpinLock      ios10 废弃
    dispatch_semaphore   建议使用,性能也比较好
    dispatch_mutex
    dispatch_queue   串行
    NSLock  对 mutex 封装
    @synchronized 性能最差
    
    

    40.追问一:自旋和互斥对比?

    什么情况使用自旋锁比较划算?
    预计线程等待锁的时间很短
    加锁的代码(临界区)经常被调用,但竞争情况很少发生
    CPU资源不紧张
    多核处理器
    
    什么情况使用互斥锁比较划算?
    预计线程等待锁的时间较长
    单核处理器
    临界区有IO操作
    临界区代码复杂或者循环量大
    临界区竞争非常激烈   
    
    

    41.追问二:使用以上锁需要注意哪些?

    注意死锁
    在串行队列使用同步,容易造成死锁
    
    

    42.追问三:用C/OC/C++,任选其一,实现自旋或互斥?口述即可!

    两种锁的加锁原理:
    
    互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。
    
    自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,
    
    

    43.请问下面代码的打印结果是什么?

    6.png
    打印 1,3
    performSelector after 是基于 timer 定制器,定时器又是基于 runloop 实现的
    任务2在子线程中,子线程默认 runloop 是不开启的,所以不执行2
    
    

    44.请问下面代码的打印结果是什么?

    7.png
    打印1
    start 执行完,线程就销毁了.任务 test 没法执行了
    
    

    45.使用CADisplayLink、NSTimer有什么注意点?

    CADisplayLink 保证调用频率和刷帧频率一直,60FPS, 不用设置时间间隔,每秒钟60次
        可以使用 proxy 代理解决循环引用
    
        CADisplayLink、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用
    
    

    46.介绍下内存的几大区域

    低地址-> 高地址
    保留->代码段->数据段(字符串常量,已初始化全局数据,未初始化数据)>堆->栈内存-> 内核区域
    代码段: 编译之后的代码
    数据段: 字符串常量,已经初始化的全局变量,或者静态变量,未初始化的全局变量,静态变量
    堆 (低>高)  通过 alloc malloc calloc 动态分配的内存
    
    栈 (高地址 从 低地址)  函数调用开销()
    
    

    47.讲一下你对 iOS 内存管理的理解

    在iOS中,使用引用计数来管理OC对象的内存
    
    一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
    
    调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
    
    内存管理的经验总结
    当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
    想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1
    
    可以通过以下私有函数来查看自动释放池的情况
    extern void _objc_autoreleasePoolPrint(void);
    
    

    48.ARC 都帮我们做了什么?

    LLVM + Runtime 会为我们代码自动插入 retain 和 release 以及 autorelease等代码,不需要我们手动管理
    
    

    49.weak指针的实现原理

    Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。
    
    runtime对注册的类, 会进行布局,对于weak对象会放入一个hash表中。 用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候会dealloc,假如weak指向的对象内存地址是a,那么就会以a为键, 在这个weak表中搜索,找到所有以a为键的weak对象,从而设置为nil。
    
    

    50.autorelease对象在什么时机会被调用release

    iOS在主线程的Runloop中注册了2个Observer
    -第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
    -第2个Observer
        监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
        监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()
    
        objc_autoreleasePoolPop()调用时候回给 pool 中的对象发送一次 release 消息
    
    

    51.方法里有局部对象, 出了方法后会立即释放吗

    如果是普通的 局部对象 会立即释放
    如果是放在了 autoreleasePool 自动释放吃,则会等runloop 循环,进入休眠前释放
    
    

    52.思考以下2段代码能发生什么事?有什么区别?

    8.png
    第一个内存会暴涨,self.name 会不停的创建
    第二个内存固定,会使用 Tagged Pointer 将值存在地址中
    
    

    53.你在项目中是怎么优化内存的?

    内存优化可以从 内存泄漏 和 内存开销 2方面入口
    
    - 减少内存泄露
      可以使用静态分析以及instruments的leaks 分析
      注意 NStimer 以及 block ,delegate 等的使用,避免循环引用
    
    - 降低内存使用峰值
      1. 关于图片加载占用内存问题:imageNamed: 方法会在内存中缓存图片,用于常用的图片。
       imageWithContentsOfFile: 方法在视图销毁的时候会释放图片占用的内存,适合不常用的大图等。
    
      2.tableView cell 尽量使用重用机制,减少额外的开销
      3.tableView 列表图片展示尽量使用缩略图
      4.延迟加载 对象,节约内存开销
      5.避免短时间大量创建对象,配合 autoreleasePool 减少内存峰值
      6.重用大开销对象,比如: NSDateFormatter和NSCalendar
      7.加载 html 尽量使用 wkwebView
      8.单例使用不易过多
      9.线程最大并发数
    
    

    54.优化你是从哪几方面着手?

    卡顿优化
    启动优化
    耗电量优化
    app 瘦身
    
    CPU 占用率、 内存使用情况、网络状况监控、启动时闪退、卡顿、FPS、使用时崩溃、耗电量监控、流量监控....
    
    

    55.列表卡顿的原因可能有哪些?你平时是怎么优化的?

    1.最常用的就是cell的重用, 注册重用标识符
       如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell;
       如果有很多数据的时候,就会堆积很多cell。
       如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell
    
    2.避免cell的重新布局
       cell的布局填充等操作 比较耗时,一般创建时就布局好
       如可以将cell单独放到一个自定义类,初始化时就布局好
    
    3.提前计算并缓存cell的属性及内容
        当我们创建cell的数据源方法时,编译器并不是先创建cell 再定cell的高度
        而是先根据内容一次确定每一个cell的高度,高度确定后,再创建要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提前估算高度告诉编译器,编译器知道高度后,紧接着就会创建cell,这时再调用高度的具体计算方法,这样可以方式浪费时间去计算显示以外的cell
    
    4.减少cell中控件的数量
       尽量使cell得布局大致相同,不同风格的cell可以使用不用的重用标识符,初始化时添加控件,
       不适用的可以先隐藏
    
    5.不要使用ClearColor,无背景色,透明度也不要设置为0
       渲染耗时比较长
    
    6.使用局部更新
       如果只是更新某组的话,使用reloadSection进行局部更新
    
    7.加载网络数据,下载图片,使用异步加载,并缓存
    
    8.少使用addView 给cell动态添加view
    
    9.按需加载cell,cell滚动很快时,只加载范围内的cell
    
    10.不要实现无用的代理方法,tableView只遵守两个协议
    
    11.缓存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这两者同时存在才会出现“窜动”的bug。所以我的建议是:只要是固定行高就写预估行高来减少行高调用次数提升性能。如果是动态行高就不要写预估方法了,用一个行高的缓存字典来减少代码的调用次数即可
    
    12.不要做多余的绘制工作。在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。
    
    13.预渲染图像。当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;
    
    14.使用正确的数据结构来存储数据。
    
    

    56.app 启动优化

    1\. pre-main 之前
    
        * 排查无用的动态库(定期清理)
        * 减少ObjC类(项目中不适用的的库,废弃的代码等)、方法(selector)、分类(category)的数量、无用的库
        * 少在类的+load方法里做事情,尽量把这些事情推迟到+initiailize1.
    
    2\. main 函数之后的 didFinishLaunchingWithOptions 加载完之前
    
        * 不影响用户体验的操作,做延迟加载,不要全部放在  didFinishLaunchingWithOptions中去做
        * 版本更新,一些三方初始化,不需要在 didFinishLaunchingWithOptions 初始化的放到,界面展示完以后再初始化
        * 一些网络请求延迟 请求..
        * 一些业务逻辑延迟 加载
        * 初始化第三方 SDK
        * 配置 APP 运行需要的环境
        * 自己的一些工具类的初始化
    
    

    57.app 耗电量优化

    1.不要频繁的刷新页面,能刷新1行cell最好只刷新一行,尽量不要使用reloadData.
    2.选择正确的集合
        NSArray,使用index来查找很快(插入和删除很慢)
        字典,使用键来查找很快
        NSSets,是无序的,用键查找很快,插入/删除很快
    3.少用运算获得圆角,必须要用圆角的话,不如把图片本身就做成圆角
    4.懒加载,不要一次性创建所有的subview,而是需要时才创建.
    5.重用机制
    6.图片处理
        图片与imageView相同大小,避免多余运算
        可以使用整副的图片,增加应用体积,但是节省CPU
        可调大小的图片,可以省去一些不必要的空间
        CALayer,CoreGraphics,甚至OpenGL来绘制,消耗CPU
    7.cache,cache,cache(缓存所有需要的)
        服务器相应结果的缓存(图片)
        复杂计算结果的缓存(UITableView的行高)
    8.尽量少用透明或半透明,会产生额外的运算.
    
    9.使用ARC减少内存失误,dealloc需要重写并对属性置为nil
    
    10.避免庞大的xib,storyBoard,尽量使用纯代码开发
    
    CPU层面
    
    1.Timer的时间间隔不宜太短,满足需求即可
    2.线程适量,不宜过多,不要阻塞主线程
    3.优化算法,减少循环次数
    4.定位和蓝牙按需取用,定位之后要关闭或降低定位频率
    5.一些硬件的使用,不使用就关掉
    
    

    58.app 的包瘦身

    9.png

    59.讲讲 MVC、MVVM、MVP,以及你在项目里具体是怎么写的?

    MVC  Model-view-controller 数据-视图-控制器     
    一般控制器用于管理数据和视图, 数据和视图交互都是通过控制器来进行的.视图和数据进行了解耦, 但是我们日常使用经常会将模型绑定给视图.模型封装在视图内部,外部不用管理视图内部业务逻辑,这数据 mvc 的变种, 控制器只给视图模型数据就好了. 缺点是视图和 模型有耦合;
    
    MVVM Model-view-viewModel  模型-视图-视图模型
    view 和 model 的交互通过viewmodel 来进行交互,实现数据的双向绑定
    
    MVP  Model-view - Presenter  模型-视图-主持人
    
    view 和 model 的交互通过Presenter,controller通过Presenter来管理 model 和 View
    
    

    60.你自己用过哪些设计模式?

    结合自己项目来讲吧
    
    

    61.一般开始做一个项目,你的架构是如何思考的?

    根据模块,使用 mvc 功能划分..结合自己项目讲比较容易
    涉及到东西也比较多,比较杂,大到整个项目架构,小到一个 view 的架构;没具体的答案
    

    相关文章

      网友评论

        本文标题:[iOS]底层原理五 : (面试题目整理)

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