回顾OC基础2
2019-03-02 本文已影响0人
懒惰的习惯
// 1. _objc_MsgForward函数是做什么的?直接调用它,会发生什么?
/** 我的看法:
* 1> _objc_MsgForward函数是一个IMP,也即是一个方法实现,当一个方法并没有被实现的时
候,会经过几个过程,其中之一就是_objc_MsgFordward,抓发给对应的对象去实现它
* 2> 直接调用会造成,原先实现的方法不管用,而直接调用_objc_MsgForward方法的实现。
*/
// 2. 能否向编译编译后得到的类中添加实例变量?能否向运行时创建的类中添加实例变量?为什么?
/** 我的看法:
* 1> 不能向编译后得到的类中添加实例变量
* 2> 能向运行时创建的类中添加实例变量
* 原因:
* 1> 因为编译后得到的类已经通过runtime确定了实例变量的内存大小和链表结构,同时也会调用
runtime方法来实现strong、weak。
* 2> 可以通过runtime中的class_addIvar,来添加实例变量,但要在class_allocateClassPair之后,class_registerClassPair之前。
*/
// 3. runloop和线程有什么关系?
/** 我的看法:
* 1> 除了主线程和主runloop是默认启动外,其他的runloop都是需要手动启动的。
* 2> 我们的应用之所以不崩溃就是因为主runloop一直在默认无限循环中,直到被破坏而程序关
闭。
* 3> runloop跟线程是相互协调、相互生存的,可以说,runloop是为线程而生,线程而死。在Foundation中可以通过[NSRunLoop currentRunLoop]来创建当前runloop
*/
// 4. runloop的mode有啥作用?
/** 我的看法:
* 1> mode有4种:默认、启动、滚动、集合(common)这4种。
* 2> 可以更好的给程序提供性能,合理的利用资源,比如在滚动的时候,进入滚动模式,让
runloop中其他模式的代码暂停。
*/
// 5. 为什么oc对象要放在堆中,而不是栈?
/** 1> 如何实现一个栈OC对象?
* struct {
Class isa;
} fakeNSObject;
fakeNSObject.isa = [NSObject class];
NSObject *obj = (__bridge NSObject *)&fakeNSObject;
NSLog(@"obj = %@", [obj description]); // 栈对象
NSObject *obj2 = [[NSObject alloc] init];
NSLog(@"obj2 = %@", [obj2 description]); // 堆对象
* 2> 栈对象的生命周期固定,虽然不会造成内存泄漏,但是在{}结束后,栈对象就被pop出去了,
不能被长时间引用
* 3> 栈对象内存比较小,因为iOS自动管理,而且不会长时间引用,因此内存空间有限
* 4> block实际上是一个栈对象,但在ARC中,会自动copy在堆中,MRC需要手动copy
*/
// 6. instancetype和id的区别?
/** 我的看法:
* 1> instancetype只能作为方法的返回,不能做参数,而id都可以
* 2> instancetype返回的是本类对象(所在类),id是未可知类对象
* 3> instancetype可以更好的为开发者在编译阶段时处理语法错发,而id可以更好的实现对象的
传递
*/
// 7. objc是使用什么机制来管理对象内存的?
/** 我的看法:
* 1> 通过retainCount来管理的
* 2> 无论在ARC中,还是MRC中,都是这样
* 3> 在销毁对象时,ARC是在当前运行循环结束时,检查retainCount,如果为0就销毁,MRC是
手动销毁。
*/
// 8. 不手动指定@autoreleasepool,一个@autoreleasepool什么时候释放?(以一个viewDidLoad为例)
/** 我的看法:
* 1> 手动添加@autoreleasepool后,在{}结束了释放。
* 2> 在主autoreleasepool下,当用户唤醒(系统事件)app时就自动开启运行循环,在当
前运行循环结束时,释放autoreleasepool中的对象,给它发release。
* 3> viewDidLoad中,如果添加autoreleasepool,则在{}结束后,释放,也就是在
viewDidLoad运行完之前,如果是主autoreleasepool,则在当前运行循环结束后,释
放。
*/
// 9. BAD_ACCESS在什么情况下出现?
/** 我的想法:
* 1> 这叫悬垂指针,也就是访问了已经释放的内存,对已经释放的内存发送消息。
*/
// 10. 苹果是如何实现autoreleasepool的?
/** 我的看法:
* 1> 苹果是通过3组数组来实现的pop、push、release。
* 2> 也就是放入autoreleasepool的对象push进这个push数组。
* 3> pop数组存放已经出autoreleasepool里的对象。
* 4> release数组存放需要释放的对象。
*/
// 11. 在block内如何修改block的外部变量?
/** 我的看法:
* 1> block在iOS中是放入堆内存的,所以除非主动释放,否则会一直存在内存中
* 2> 不是全局变量、静态变量、__block修饰变量的一些block外部变量是放入栈内存中
* 3> 而栈内存在iOS中只有1M,堆内存大很多,所以栈内存中的值,修改后也不会有效果,在
block{}结束后就被释放掉了,属于值传递
* 4> 要想对block外部变量的值进行修改,只有将外部变量值先放入堆内存中,才有效。
*/
// 12. 使用系统api时,比如UIView的动画block是否也要注意循环引用?
/** 我的看法:
* 1> 使用系统api时,不带ivar参数的block一般是不用注意循环引用的,系统会做处理
* 2> 如果使用了ivar的系统block,比如GCD的group,NSNotification等,要注意
* 3> 可以用facebook的循环引用检测开源库,来检测一下。
*/
// 13. GCD的队列分几种?
/** 我的看法:
* 1> 串行队列 serial dispatch queue
* 2> 并发队列 concurrent dispatch queue
*/
// 14. 如何用GCD同步若干个异步操作?
/** 我的看法:
* 1> 采用group
* 2> group_async进行异步操作,队列用并发队列concurrent dispatch queue或全局队列
* 3> 然后用group_notify合并一下操作里的内容
*/
// 15. dispatch_barrier_async的作用是什么?
/** 我的看法:
* 1> dispatch_barrier_async是阻止当前并发队列的其他操作,等待队列中的任务完成
* 2> 完成后,在执行dispatch_barrier_queue中的任务
* 3> dispatch_barrier_async中的并发队列只能是自定义,如果是系统的全局队列会失效,dispatch_barrier_async的作用会跟dispatch_aysnc一样了。
*/
// 16. 苹果为什么废弃dispatch_get_current_queue?
/** 我的看法:
* 1> 容易造成程序死锁
*/
// 17. KVC的keypath中的集合运算符如何试用?
/** 我的看法:
* 1> 集合运算符必须使用在普通对象或集合对象上
* 2> 集合运算符主要有@min、@max、@avg、@sum、@count
* 3> 集合运算符的格式主要是普通对象中属性age,@"@sum.age"或集合对象中普通对象属性
@"obj.@sun.age"。
*/
// 18. KVO/KVC中的keypath一定是属性吗?
/** 我的看法:
* 1> kvc都可以
* 2> kvo因为要触发observe,所以自动只支持属性,手动支持实例变量或ivar
*/
// 19. category是如何实现的?
/** 我的看法:
* 1> category是运行时决定的,可以添加方法(包括类方法)、属性、但不能添加实例变量
* 2> 实例变量是在编译时就已经决定了,编译时已经决定了对象的内存结构,如果添加实例变量会
改变其内存结构。
* 3> 如果利用运行时给对象添加方法,则需要在alloc之后,register之前
* 4> category其实并没有覆盖掉原来的方法,只是排在了原来方法的前面,运行时在查到方法
时,是按顺序查找,因此才会调用category里的方法实现。
* 5> load方法是编译时决定的,在类、category里添加load方法,都会执行,只是执行顺序是
根据编译顺序决定的。
*/
// 20. GCD和NSOperation
/** 我的看法:
* 1> 这是多线程开发技术,这两者技术是跟多线程无关,因为iOS会自己操作多线程
* 2> 多线程就是一个任务在一条线程上执行,还是分在多个线程,执行小任务,然后合成
* 3> 多线程是不是越多越好?每开一个线程都要损失一定的性能和资源,它有自己的栈和寄存器
* 4> 真正让多线程能提高效率的,是并行技术,它可以让并发任务利用并行技术,异步执行,这样
才会合理的利用CPU
* 5> GCD是C实现的,和NSOperation相比,效率会更高,并且大部分函数是线程安全的
* 6> 其中GCD和NSOperation都封装了多线程,只开放给我们队列和任务,也就是我们根本不用管
多线程的开启和死亡,这个技术会自己管理。
* 7> GCD除了队列,还有一个同步、异步的概念,也就是顺序执行和同时执行,一般我们没有特殊
要求,都用异步
* 8> GCD的队列有系统给的,和自己创建这两种,一般我建议自己创建,因为自己创建在调试的时
候会有名字,而且效率也会高很多,而且自己创建的不用管理内存资源,ARC会自己管理
* 9> GCD有once、gruop、信号量、barrier、wait、after等技术
* 10> NSOperation是封装了GCD的Foundation框架,基础功能比GCD要多,比如取消、依赖、
暂停、优先级等,高级一点的是自定义NSOperation
*/
// 21. lldb(gdb)常用的调试命令?
/** 我的看法:
* 1> po,打印对象
* 2> breakpoint set .. 设置断点
* 3> n 断点下一步
*/
// 22. 出现BAD_ACCESS,如何调试
/** 1> xcode设置僵尸对象
* 2> 断点调试
* 3> 利用runtime的responseToSelector方法,出现BAD_ACCESS时objc最后访问的一个方法
*/
// 23. 如何理解函数式编程和响应式编程?
/** 我的看法:
* 1> 函数式编程就是点".",所有的一些方法调用,基本可以用点语法来代替,而出现少量的[],
或基本没有。例如masonry
* 2> 响应式编程就是一个值的变化引起了另一个的变化,比如a = b + c,只要b或c变化,a立马
得到响应。比如RAC就是经典例子
* 3> RAC就是将iOS原生的响应全部通过信号量(single)来接收,然后函数式编程,直接在后面的
block当中处理响应事件,比如RACObserve(kvo),[text rac_textsingle](输入框
值的更新的监听),组合信号的监听([RACsingle combineLatest]),按钮的监听[button rac_singleForControlEvents],代理的监听等等
* 4> 重点注意rac中block的循环引用
*/
// 24. drawInRect是内存恶鬼吗?
/** 我的看法:
* 1> drawInRect实际是采用上下文来处理你所画的图
* 2> 当你的上下文太大,而每次又需要重新绘制时,内存就会占用很多
* 3> UIView显示实际上是CALayer,而drawInRect所绘制的图,是一张一张的存储在CALayer
上,当你需要显示哪张是就在CALayer上显示已缓存的那些图中的其中之一
* 4> 因此当你采用drawInRect绘制图时,应当使上下文尽可能的小,主要跟scale * width *
height 有关
* 5> 如果上下文是在太大,可以使用专用图层(CAShaperLayer)代替
*/