美文网首页iOS 面试专题
iOS MJ讲解面试题

iOS MJ讲解面试题

作者: Barry_小闪 | 来源:发表于2016-07-15 12:00 被阅读405次

    1、KVO内部实现原理

    • 1.KVO是基于runtime机制实现的
    • 2.当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法。
      派生类在被重写的setter方法实现真正的通知机制(Person->NSKVONotifying Person)

    2、是否可以把比较耗时的操作放在NSNotificationCenter中

    • 通知中心所做的操作在主线程,比较耗时的一般开启一个线程单独去跑

    3、KVO、NSNotification、代理的区别和用法是什么?什么时候该用那个?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果能,会有什么潜在的问题?如果不能,为什么?

    答案:

    参考:http://blog.csdn.net/dqjyong/article/details/7685933

    通知(NSNotification):
    • 比较灵活(1个通知能被多个对象接受,多个对象能接受多个通知)
    • 没有耦合性(A发出的通知,谁去接收并不知道)
    • 缺点通知的Key容易被写错
    KVO
    • 性能不好(底层对会Runtime动态产成新的类NSNotifying_xx)
    • 局限性(只能监听某个对象属性的改变,不推荐实用)
    • 1个对象的属性能被多个对象监听,1个对象能监听多个对象的其他属性
    代理
    • 比较规范,所以安全性高
    • 默认是1对1监听,可以用代理数组 a.delegates = @[b, c, d];(千万别这样写,违背的设计模式,这样写还不如同消息通知)

    4、Runtime实现的机制是什么,怎么用,一般用于干嘛,你还记得你所使用的相关的头文件或者某些方法的名称吗?

    运行时机制,runtime库里面包含了跟类、成员变量、方法相关的API,比如获取类里面的所有成员变量,为类动态添加成员变量,动态改变类的方法实现,为类动态添加新的方法等,需要导入<objc/message.h><objc/Runtime.h>

    1.什么是Runtime
    • runtime是一套比较低层的纯C语言API,属于1个C语言库,包含许多C底层的C语言的API
    • 平时编写的OC代码,在程序 运行过程中,其实最终都转换成了runtime的C语言代码,runtime算是OC的幕后工作者
      举例:
      OC:
      [MJPerson alloc]init];
      runyime:
      objc_msgSend(objc_msgSend("MJPerson", "alloc"), "init")
    2.用过么?怎么用?
    • 1.runtime是属于OC底层,可以进行 一些非常的操作(OC无法实现的,不容易实现的)
    • 在程序运行过程中,动态创建一个类(比如KVO底层实现)
    • 在程序运行过程中,动态地为某个类 添加属性、方法,修改属性值、方法
    • 便利一个类的 所以成员变量(属性)、方法
    3.相关应用
    • 1.头文件
      <objc/runtim.h>
      <objc/message.h> 调用底层的消息发送方法
    • 2.相关应用
    • NSCoding(归档和解档,利用runtime遍历模型对象的所以属性)
    • 字典 ->模型 (利用runtime遍历模型对象的所以属性,根据属性名从字典中取出对应的值,设置到模型的属性上)
    • KVO(利用runtime动态产生一个类)
    • 用于封装框架(想怎么改,就怎么改)
    • 3.相关函数
    • objc_msgSend : 给对象发送消息
    • class_copyMethodList : 遍历某个所有方法
    • class_copyIvarList : 遍历某个类所以的成员变量
    • class_addMethod : 动态添加方法
    • class_addIval :动态添加成员变量
    • class_.... class_开头的方法,是操作类的
    例:利用Runtime归档和解档
    //  MJPerson.m
    //  runtime进行归档和解档
    //
    //  Created by Zhanbo on 16/7/7.
    //  Copyright © 2016年 Zhanbo. All rights reserved.
    //
    
    #import "MJPerson.h"
    #import <objc/runtime.h>
    @implementation MJPerson
    
    - (void)encodeWithCoder:(NSCoder *)aCoder {
        
       unsigned int count = 0;
      //参数1:那个类
      //参数2:有几个成员变量
       Ivar *ivars = class_copyIvarList([MJPerson class], &count);
        
        for (int i = 0; i <count; i++) {
            //取出i位置对应的成员变量
            Ivar ivar = ivars[i];
            
            //查看成员变量
            const char *name = ivar_getName(ivar);
            NSLog(@"%s", name);
            
            //归档
            NSString *key = [NSString stringWithUTF8String:name];
            id value = [self valueForKey:key];
            [aCoder encodeObject:value forKey:key];
            
        }
        //在C语言中使用copy要释放
        free(ivars);
    }
    
    - (instancetype)initWithCoder:(NSCoder *)aDecoder {
        
        if (self = [super init]) {
           
            unsigned int count = 0;
            Ivar *ivars = class_copyIvarList([MJPerson class], &count);
            
            for (int i = 0; i <count; i++) {
                //取出i位置对应的成员变量
                Ivar ivar = ivars[i];
                
                //查看成员变量
                const char *name = ivar_getName(ivar);
                NSLog(@"%s", name);
                
                //解档
                NSString *key = [NSString stringWithUTF8String:name];
                id value = [aDecoder decodeObjectForKey:key];
                
                //设置到成员变量身上
                [self setValue:value forKey:key];
                
            }
            //在C语言中使用copy要释放
            free(ivars);
        }
        return self;
    }
    
    @end
    
    4.必备常识
    • 1.Ivar : 成员变量
      1. Method : 成员方法

    5、Foundation对象与Core Foundation对象有什么区别?

    • 1.Foundation对象是OC的,Core Foundation对象是C对象
    • 2.数据类型之间的转换(桥接)
    • ARC:bridge_retained、 _bridge_transfer
     CFArrayRef array3 = CFArrayCreate(NULL, NULL, 10, NULL);
        
        //Foundetion -> Core Fountdation
        (__bridge_retained <#CF type#>)<#expression#>
        
        //Core Fountdation -> Foundetion
        (__bridge_transfer <#Objective-C type#>)<#expression#>
        NSArray *array4 = (__bridge_transfer NSArray *)array3);
    
    • MRC: _bridge
    Foundation -> Core Foundation
    NSArray *array1 = [NSArray array];
    CFArrayRef array2 = (__bridge CFArrayRef)array1;
    //记得释放内存
    [array1 release];
    

    6、不用中间变量,用两种方法交换A和B的值

      1. A = A + B
        B = A - B
        A = A- B
    • 2.使用位运算^能交换两个变量的值

    7、 什么是动态,举例说名

    • 1.在线程运行过程才执行的操作,如Runtime动态创建成员变量和方法

    8、什么是多态

    • 1.父类指针指向子类对象
    NSObject *obj = [NSArray array];
    

    9、怎么解决缓存池满的问题(cell)

    • iOS中不存在缓存池满的情况,应为通常我们在iOS开发,对象都是在需要的时候才会创建,有种常用的说法叫懒加载,还有在UITableView中一般只会创建刚开始出现在屏幕中的cell,之后都是从缓存池里取,不会创建新对象,缓存池里最多就一两个对象,缓存池满的这中情况一般在开发Java中比较常见,Java中一般把最近最少使用的对象先释放
    • 如果缓存池满了,判断哪个位置的cell是不经常使用的,先释放掉

    10、如何渲染自定义格式字符串的UILabel

    • 1.通过NSAttributedString类
        //1种写法,也可以用字典
     NSMutableAttributedString *str = [[NSMutableAttributedString alloc]initWithString:@"哈哈哈啦啦啦哇哇哇"];
        //设置文字颜色
        [str addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 3)];
        [str addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(6, 3)];
        //设置字体
        [str addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:30] range:NSMakeRange(3, 3)];
        
        _label.attributedText = str;
    
    效果

    11、scrollView的contentSize能在ViewDidLoad里设置吗,为什么?

    • 1.能,在哪里都能设置
    • 2.但是最好不要在viewDidLoad里设置,因为视图刚创建不一定是我们想要的尺寸,在iPad开发中横竖屏是很好的例子

    12、控制器View的生命周期及相关函数什么?你在开发中是如何用的?

    • 1.首先判断控制器是否有视图,如果没有就调用loadView方法创建:通过storyboard或者代码
    • 2.随后调用viewDidLoad,可以进行下一步的初始化操作:只会被调用一次
    • 3.在视图显示之前(即将显示)调用viewWillAppear,该函数可以多次调用
    • 4.在视图显示,会调用viewDidAppear
    • 5.视图即将消失,调用viewWillDisappear
    • 6.视图已经消失,调用viewDidDisappear
    • 7.在布局变化前后,调用viewWillDidLayoutSubviews处理相关信息

    13、Block的内存管理

    • 1.默认情况下,block的内存是在栈中
    • 它不会对所引用的对象进行任何操作
    • 2.如果对block做一次copy操作,block的内存就会在堆中
    • 它会对所引用的对象做一次retain操作
    • ARC :如果所引用的对象用了__unsafe_unretained或者__weak修饰,就不会做retain操作
    • 非ARC(MRC) : 如果所引用的对象用__block修饰,就不会做retain操作;MRC记得释放blockBlock_release(_block);
    
    - (void)dealloc {
        //MRC记得在dealloc释放blcok,[super dealloc];要写最后
        Block_release(_block);
        [super dealloc];
    }
    

    14、MRC中如何做开发

    1.MRC中retain相当于ARC中strong
    assign相当于ARC中weak
    2.只要声明对象属性就要写retain

    @property (nonatomic, retain)NSArray *array; +1(计数器 = 1)
    

    3.声明控件、协议用assgin

    4.MRC的内存管理原则

    • 如果调用了alloc、new、copy产生了一个新对象,最后肯定要调用1次releace或者autorelease
    • 如果让一个对象做了retain操作(计数器+1),最后肯定要调用1次release或者autorelease
    • 原则:有+ 就有-
    self.array = [NSArray alloc]init]autorelease];
    alloc后 +1(计数器 = 2); autorelease后 -1(计数器 = 1)
    //或者这样写,就不需要写autorelease,系统内部自动写好
    self.array = [NSArray array];
    
    • 最后要在dealloc释放内存
    //当控制器将被释放的时候对调用
    - (void)dealloc {
    //最好这样写
    self.array = nil;
    -1(计数器 = 0)
    
    //也可以这样写
    self.array = [array release];
    //最后调用
    [supe dealloc];
    }
    

    如果是iOS版本在5.0之前要在3个地方释放内存

    //控制器的View被卸载,会调用
    - (void)viewDidUnload {
        
        [super viewDidUnload];
        self.array = nil;
    }
    
    //接收到内存警告,会调用
    - (void)didReceiveMemoryWarning {
        
        [super didReceiveMemoryWarning];
        self.array = nil;
    }
    
    - (void)dealloc {
        
        self.array = nil;
        
        [super dealloc];
    }
    

    15、多线程如何进行线之间通讯?

    //在主线程执行某个方法,传参(该方法用于子线程对主线程进行通讯)
    [self performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]
    
    //指定线程,调用方法,传参
    [self performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#> modes:<#(nullable NSArray<NSString *> *)#>
    
    //GCD在异步线程做事情
    dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
    

    16、用NSOperation和NSOperationQueue处理A,B,C三个线程,要求执行完A,B才能执行C,怎么做?

    • 1.第一种方法添加依赖
    //创建列队
    NSOperationQueue *queue = [NSOperationQueue alloc]init];
    //创建3个操作
    NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation1....");
    }];
    
    NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation2....");
    }];
    
    NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation3....");
    }];
    //======添加依赖=======
    //只有当a操作执行完毕后,才会执行c操作
    [c addDependency:a];
    //只有当b操作执行完毕后,才会执行c操作
    [c addDependency:b];
    
    [queue addOperation:a];
    [queue addOperation:b];
    [queue addOperation:c];
    
    
    • 2.第二种方法 可 以设置优先级
    - (NSOperationQueuePriority)queuePriority;
    

    17、GCD内部怎么实现的

    • 1.iSO和OS X的核心是XNU内核,GCD是基于XNU内核实现的
    • 2.GCD的API全部在libdispatch库中
    • 3.GCD的底层实现主要有Dispatch Queue和 Dispatch Source
    • Disatch Queue :管理block(操作,block里的代码块,根据类型在哪个线程执行)
    • Disatch Source : 处理事件(底层实现,处理线程之间的事件,如主线程会到什么线程,异步线程回到什么线程)

    18、NSOperatinQueue和CGD的区别,什么情况下用NSOperationQueue,什么情况下用GCD

    • 1.GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装
    • 2.GCD只支持FIFO的列队(先进先出),NSOperationQueue可以很方便地调整执行顺序(设置优先级 ),设置最大并发数量
    • 3.NSOperatinQueue可以轻松在Operation间设置依赖关系,而GCD需要些很多代码才能实现
    • 4.NSOperationQueue支持KVO,可以监听operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld);系统内部已经做好的KVO
    • 5.GCD的执行速度比NSOperationQueue快
    • 什么时候,用哪个
    • 1.任务之间有依赖/或者要监听任务的执行情况:NSOperatinQueue(任务需要时刻监听;任务严格需要按顺序执行)
    • 2.任务之间不太相互依赖就用:CGD

    19、既然说到GCD,那么问下在使用GCD已经block需要注意什么?

    • 1.GCD的注意18题已有说明
    • 2.Block的使用注意
    • 1.block的内存管理(13题已有说明)
    • 2.防止循环retain
      • ARC:__weak/__unsafe_unretained
      • MRC: __block 0

    20、如果后期需要增加数据库中的字段怎么实现,如果不使用CoreData呢?

    编写SQL语句来操作原来表中的字段

    • 1.增加表字段
      ALTER TABLE 表名 DROP COLUMN 字段名 字段类型
    • 2.删除表字段
      ALTER TABLE 表名 DROP COLUMN 字段名
    • 3.修改表字段
      ALTER TABLE 表名 RENAME COLUMN 旧字段名 TO 新字段名

    21、简单描述下客户端的缓存机制

    • 1.缓存可以分为:内存数据缓存、数据库缓存、文件缓存
    • 2.每次想获取数据的时候
    • 先检测内存中有无缓存
    • 在检测本地有无缓存(数据库/文件)
    • 最终发送网络请求
    • 将服务器返回的网络数据进行缓存(内存、数据库、文件),已便下次读取

    22、有些图片加载的比较慢怎么处理?你是怎么优化程序的性能的?

    • 1.图片下载放在异步线程
    • 2.图片下载过程中使用占位图,提高用户的天
    • 3.如果图片比较大,可以考虑多线程断点下载(开辟多个线程下载一张图片,需要服务器设置请求体信息)

    23、你实现过一个框架或者库以供别人使用吗?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。

    • 1.提供给外界的接口功能是否实用、够用
    • 2.别人使用我的框架时,能不能根据类名、方法名就猜出接口的具体作用
    • 3.别人调用接口时,提供的参数是否够用、调用起来是否简单
    • 4.别人使用我的框架时,要不要再导入依赖其他的框架

    24、是否可以把比较耗时的操作放在NSNotificationCenter中?

    • 1.如果在异步线程发的通知,那么可以执行比较耗时的操作
    • 2.如果在主线程发的通知,那么就不可以执行比较耗时的操作
      补充:NSNotificationCenter默认是主线程的,但是在异步线程发通知,那么NSNotificationCenter就会变成异步线程

    25、SDWebImage具体如何实现

    • 1.利用NSOperationQueue和NSOperation下载图片,还使用了GCD的一些函数(解码GIF图片)
    • 2.利用URL作为key,NSOperation作为value
    • 3.利用URL作为key,UIImage作为value

    26、怎么解决sqlite锁定的问题

    • 1.设置数据库锁定的处理函数
    当数据库被锁时,会调用该方法
    //参数1传入数据库,
    //参数2传入函数名(在该函数中做操作)
    int sqlite3_busy_handler(sqlite3*, test);
    
    • 2.设置锁定时的等待时间
    int sqlite3_busy_timeout(sqlite3*, int ms);
    

    27、cocoa中常见对几种多线程的实现,并谈谈多线程安全的几种解决办法及多线程安全怎么控制?

    • 1.只在主线程刷新访问UI
    • 2.如果要防止资源抢夺,得用synchroized进行加锁保护
    • 3.如果异步操作要保证线程安全等问题,尽量使用GCD(有些函数默认就是安全的)

    28、什么是run loop?

    • 1.runloop是消息循环,内部有定时源和输入源来运作
    • 2.在创建的程序不需要显示的创建run loop;每个线程,包括程序的主线程(main thread)都有与之相应的run loop对象,主线程会自行创建并运行run loop
    • 3.run loop处理的输入事件有两种不同的来源:输入源(input source)和定时源(timer source)
    • 4.输入源处理传递异步消息,通常来自于其他线程或者程序。定时源则处理传递同步消息,在特定时间或者一定的时间间隔发生

    Block中内存情况

    • 1.默认情况下,block的内存是在栈中,它不会对所引用的对象进行任何操作
    • 2.如果对block做一次copy操作,block的内存就会在堆中
    • 它会对所引用的对象做一次retain操作
    • 非ARC(MAC) :如果所引用的对象用了__block修饰,就不会做retain操作
    • ARC :如果所引用的对象用了__unsafe_unretained,或者__weak修饰,就不会做retain操作

    相关文章

      网友评论

        本文标题:iOS MJ讲解面试题

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