1.MVC的不足之处
1、增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。 2、视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。 3、视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
4、目前,一般高级的界面工具或构造器不支持模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成MVC使用的困难。
2.讲一讲其它架构
MVC / MVVM / VIPER
3.你知道哪些编码方式
如果直接用这个url去请求会报错,需要先转义,NSString提供方法
stringByAddingPercentEscapesUsingEncoding: 编码 stringByReplacingPercentEscapesUsingEncoding: 解码
4.字符串翻转
//字符串翻转
void reverse(char *str) {
char *begin = str;
char *end = str + strlen(str) - 1;
while (begin < end) {
char *temp = *begin;
*(begin ++) = *end;
*(end --) = temp;
}
}
5.HTTPS
http+TLS(SSL)
6.多线程的方式和它们的区别
-
Pthreads
-
NSThread
这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 [NSThread currentThread],它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。下面来看看它的一些用法。
// 创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
// 启动
[thread start];
-
GCD
Grand Central Dispatch,听名字就霸气。它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。同时它使用的也是 c语言,不过由于使用了 Block(Swift里叫做闭包)
任务和队列
在 GCD 中,加入了两个非常重要的概念:任务和队列。
- 任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行,他们之间的区别是 是否会创建新的线程。
同步执行:只要是同步执行的任务,都会在当前线程执行,不会另开线程。
异步执行:只要是异步执行的任务,都会另开线程,在别的线程执行
放到串行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。
```
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//3.3.执行5次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
```
-
NSOperation & NSOperationQueue
//1.创建一个其他队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2.创建NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //3.添加多个Block for (NSInteger i = 0; i < 5; i++) { [operation addExecutionBlock:^{ NSLog(@"第%ld次:%@", i, [NSThread currentThread]); }]; } //4.队列添加任务 [queue addOperation:operation];
7.队列和线程的关系
-
开不开线程,取决于执行任务的函数,同步不开,异步开。
-
开几条线程,取决于队列,串行开一条,并发开多条(异步)
-
主队列: 专门用来在主线程上调度任务的"队列",主队列不能在其他线程中调度任务!
-
如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!主队列同步任务,会造成死锁。原因是循环等待
-
同步任务可以队列调度多个异步任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这是依赖关系。
-
全局队列:并发,能够调度多个线程,执行效率高,但是相对费电。 串行队列效率较低,省电省流量,或者是任务之间需要依赖也可以使用串行队列。
8.线程安全
在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如,同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。
9.有哪些锁
10.属性的关键字
property的关键字分三类:
-
一类是表示原子性(也就是线程安全)的,有atomic和nonatomic,默认是atomic,acomic也就是线程安全,但是我们一般都用的nonatomic,因为atomic的线程安全开销太大,影响性能,即使需要保证线程安全,我们也可以通过自己的代码控制,而不用atomic。
-
一类是表示引用计数的,有assign(iOS5以前用unsafe_unretained),strong,weak,copy。
assign: assign用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
weak:对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
strong:对对象的强引用,会增加对象的引用计数,如果指向了一个空对象,会造成野指针,平常我们用得最多的应该也是strong了。
copy:建立一个引用计数为1的新对象,赋值时对传入值进行一份拷贝,所以使用copy关键字的时候,你将一个对象复制给该属性,该属性并不会持有那个对象,而是会创建一个新对象,并将那个对象的值拷贝给它。而使用copy关键字的对象必须要实现NSCopying协议。
unsafe_unretained:跟 weak 类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为 nil,现在基本都用weak了。 -
一类是表示读写权限的,默认是readwrite(可读可写),还有就是readonly,当你希望暴露出来的属性不能被外界修改时就需要申明为readonly。
11.assign可以用于OC对象吗,assing可以使用在对象中吗
1.weak
1> OC对象
2.assign
1> 基本数据类型
2> OC对象
3.strong
1> OC对象
4.copy
1> NSString
2> block
5.使用weak和assign修饰OC对象的区别
1> 成员变量
1) weak生成的成员变量是用__weak修饰的,比如Cat * __weak _cat;
2) assign生成的成员变量是用__unsafe_unretained修饰的Cat * __unsafe_unretained _cat;
2> __weak和__unsafe_unretained
1) 都不是强指针(不是强引用),不能保住对象的命
2) __weak : 所指向的对象销毁后,会自动变成nil指针(空指针),不再指向已经销毁的对象
3) __unsafe_unretained : 所指向的对象销毁后,仍旧指向已经销毁的对象
12.copy和strong的区别
用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
13.weak如何实现自动赋nil
dealloc中清除弱引用计数表
14.为什么不可变对象要用copy
很简单,假如有一个NSMutableString,现在用他给一个retain修饰 NSString赋值,那么只是将NSString指向了NSMutableString所指向的位置,并对NSMUtbaleString计数器加一,此时,如果对NSMutableString进行修改,也会导致NSString的值修改,原则上这是不允许的. 如果是copy修饰的NSString对象,在用NSMutableString给他赋值时,会进行深拷贝,及把内容也给拷贝了一份,两者指向不同的位置,即使改变了NSMutableString的值,NSString的值也不会改变.
所以用copy是为了安全,防止NSMutableString赋值给NSString时,前者修改引起后者值变化而用的.
15.Pod update和pod install的区别
pod update:
当你运行 pod update PODNAME 命令时,CocoaPods会帮你更新到这个库的新版本,而不需要考虑Podfile.lock里面的限制,它会更新到这个库尽可能的新版本,只要符合Podfile里面的版本限制。
如果你运行pod update,后面没有跟库的名字,CocoaPods就会更新每一个Podfile里面的库到尽可能的最新版本。
pod install :
这个是第一次在工程里面使用pods的时候使用,并且,也是每次你编辑你的Podfile(添加、移除、更新)的时候使用。
每次运行pod install命令的时候,在下载、安装新的库的同时,也会把你安装的每个库的版本都写在了Podfile.lock文件里面。这个文件记录你每个安装库的版本号,并且锁定了这些版本。
当你使用pod install它只解决了pods里面,但不在Podfile.lock文件里面的那些库之间的依赖。对于在Podfile.lock里面所列出的那些库,会下载在Podfile.lock里面明确的版本,并不会去检查是否该库有新的版本。对于还不在Podfile.lock里面的库,会找到Podfile里面描述对应版本(例如:pod "MyPod", "~>1.2")。
16.layoutIfNeeded和setNeedsLayout的区别
-
setNeedsLayout
标记为需要重新布局,异步调用layoutIfNeeded
刷新布局,不立即刷新,在下一轮runloop结束前刷新,对于这一轮runloop
之内的所有布局和UI上的更新只会刷新一次,layoutSubviews
一定会被调用。 -
layoutIfNeeded
如果有需要刷新的标记,立即调用layoutSubviews
进行布局(如果没有标记,不会调用layoutSubviews
)。
关键点
-
layoutIfNeeded
不一定会调用layoutSubviews方法。 -
setNeedsLayout
一定会调用layoutSubviews
方法(有延迟,在下一轮runloop结束前)。
如果想在当前runloop
中立即刷新,调用顺序应该是
setNeedsLayout
layoutIfNeeded
17.抓包工具抓取HTTPS的原理
HTTPS是通过一次非对称加密算法(如RSA算法)进行了协商密钥的生成与交换,然后在后续通信过程中就使用协商密钥进行对称加密通信,之所以要使用这两种加密方式的原因在于非对称加密计算量较大,如果一直使用非对称加密来传输数据的话,会影响效率。
https://www.jianshu.com/p/870451cb4eb0
18.isEqual和hash的关系
https://juejin.im/entry/587e0d5e61ff4b00650df3d1
19.SD的源码
20.bitmap的结构
21.可变数组的实现原理
22.如何hook一个对象的方法,而不影响其它对象
23.如何避免if else
24.自旋锁和互斥锁的区别
25.数组copy后里面的元素会复制一份新的吗
26.数组的浅拷贝与深拷贝
27.TCP为什么是三次握手和四次挥手
28.你平时怎么解决网络请求的依赖关系:当一个接口的请求需要依赖于另一个网络请求的结果
29.关于RAC你有怎样运用到解决不同API依赖关系
使用场景是当信号A执行完才会执行信号B,和请求的依赖很类似,例如请求A请求完毕才执行请求B,我们需要注意信号A必须要执行发送完成信号,否则信号B无法执行
//这相当于网络请求中的依赖,必须先执行完信号A才会执行信号B
//经常用作一个请求执行完毕后,才会执行另一个请求
//注意信号A必须要执行发送完成信号,否则信号B无法执行
RACSignal * concatSignal = [self.signalA concat:self.signalB]
[concatSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
- 编译链接你有了解多少
预处理,编译,汇编,链接
编译过程就是吧预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编文件
链接就是把各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行的文件
- 简单介绍下KVO的用法
当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。自然,重写的 setter 方法会负责在调用原 setter方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。
原来,这个中间类,继承自原本的那个类。不仅如此,Apple 还重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类
addObsever:value:forkey:
removeObsever
调用了valueWillChange、valueDidChange
- 你认为自动布局怎么实现的
解析:先提到系统提供的NSLayoutConstraint,再介绍Masonry怎样基于它的封装?
然而面试官继续问AutoLayout原理是?它的原理就是一个线性公式!比如,创建约束,iOS6中新加入了一个类:NSLayoutConstraint。它的约束满足这个公式:
- 编写一个函数,实现统计字符次数的功能:例如输入为aaabbccc,输出为a3b2c3
```
void compass(char str[]) {
int i = 0, cnt = 1;
while (str[i] != '\0') {
if (str[i++] == str[i]) {
cnt ++;
} else {
printf("%c%d",str[i],cnt);
cnt = 1;
}
i ++;
}
}
```
网友评论