iOS MJ讲解面试题

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操作
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,036评论 29 470
  • 1. 父类实现深拷贝时,子类如何实现深度拷贝。父类没有实现深拷贝时,子类如何实现深度拷贝。 1.1 深拷贝同浅拷贝...
    iYeso阅读 1,840评论 0 13
  • 欢迎加入iOS交流群2466454,大家互相交流学习!阿里-p6-一面 1.介绍下内存的几大区域?2.你是如何组件...
    不懂技术的爱迪生阅读 15,671评论 7 202
  • 把网上的一些结合自己面试时遇到的面试题总结了一下,以后有新的还会再加进来。 1. OC 的理解与特性 OC 作为一...
    AlaricMurray阅读 2,471评论 0 20
  • 今早,一睁眼,就被朋友圈里科比退役的消息刷爆了屏。我不爱看篮球,却在看完科比的事迹,科比的视频,看到了一群群科蜜们...
    挑兮达兮阅读 328评论 0 1