前言:这篇文章是我看李明杰老师的iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化总结所得,断断续续历时3个月左右,把课堂听的东西给作了一下笔记。git
总结不易,耗时耗力,您的一颗小星星✨是我无限的动力。原文地址github
咱们常常会看一些面试题,可是好多面试题咱们都是知其然不知其因此然,你若是认真的看了我上面总结的几十篇文章,那么你也会知其因此然。面试
一、一个NSObject对象占用多少内存?数据库
系统分配了16个字节给NSObject对象(经过malloc_size函数得到),但NSObject对象内部只使用了8个字节的空间(64bit环境下,能够经过class_getInstanceSize函数得到)编程
二、对象的isa指针指向哪里?数组
三、OC的类信息存放在哪里?缓存
具体实现请参考: 一、一个NSObject对象占用多少内存 二、OC对象的分类安全
一、iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)性能优化
_NSSetXXXValueAndNotify
函数
willChangeValueForKey
方法setAge
方法didChangeValueForKey
方法didChangeValueForKey
方法内部调用oberser的observeValueForKeyPath:ofObject:change:context:
方法二、如何手动触发KVO?bash
手动调用willChangeValueForKey:和didChangeValueForKey:
三、直接修改为员变量会触发KVO么?
不会触发KVO
具体实现请参考:三、KVO实现原理
一、经过KVC修改属性会触发KVO么?
会触发KVO,由于KVC是调用set
方法,KVO就是监听set
方法
二、KVC的赋值和取值过程是怎样的?原理是什么?
KVO的setValue:forKey原理
KVO的ValueforKey原理
具体实现请参考:四、KVC实现原理
一、Category的实现原理
二、Category和Class Extension的区别是什么?
三、load、initialize方法的区别什么?
1.调用方式
2.调用时刻
四、load、initialize的调用顺序
1.load
2.initialize
五、如何实现给分类“添加成员变量”?
默认状况下,由于分类底层结构的限制,不能添加成员变量到分类中。但能够经过关联对象来间接实现
关联对象提供了如下API
添加关联对象
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
得到关联对象
id objc_getAssociatedObject(id object, const void * key)
移除全部的关联对象
void objc_removeAssociatedObjects(id object)
复制代码
具体实现请参考: 5.一、分类的实现原理 5.二、Load和Initialize实现原理
一、block的原理是怎样的?本质是什么?
二、block的(capture)
为了保证block内部可以正常访问外部的变量,block有个变量捕获机制
三、Block类型有哪几种 block有3种类型,能够经过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
四、block的copy
在ARC环境下,编译器会根据状况自动将栈上的block复制到堆上,好比如下状况
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
复制代码
五、__block修饰符
__block
能够用于解决block内部没法修改auto变量值的问题
__block
不能修饰全局变量、静态变量(static)
编译器会将__block
变量包装成一个对象
当__block变量在栈上时,不会对指向的对象产生强引用
当__block变量被copy到堆时
若是__block变量从堆上移除
六、循环引用
__unsafe_unretained typeof(self) weakSelf = self;
self.block = ^{
print(@"%p", weakSelf);
}
复制代码
__weak typeof(self) weakSelf = self;
self.block = ^{
print(@"%p", weakSelf);
}
复制代码
__block id weakSelf = self;
self.block = ^{
weakSelf = nil;
}
self.block();
复制代码
具体实现请参考:六、Block底层解密
一、讲一下 OC 的消息机制
二、消息转发机制流程
消息发送阶段
消息发送流程是咱们平时最常用的流程,其余的像动态方法解析和消息转发实际上是补救措施。具体流程以下
动态方法解析
开发者能够实现如下方法,来动态添加方法实现
+resolveInstanceMethod:
+resolveClassMethod:
动态解析事后,会从新走“消息发送”的流程,从receiverClass的cache中查找方法这一步开始执行消息转发
若是方法一个方法在消息发送阶段没有找到相关方法,也没有进行动态方法解析,这个时候就会走到消息转发阶段了。
forwardingTargetForSelector
,返回值不为nil时,会调用objc_msgSend
(返回值, SEL)methodSignatureForSelector
,返回值不为nil,调用forwardInvocation:方法;返回值为nil时,调用doesNotRecognizeSelector:
方法forwardInvocation:
方法中自定义任何逻辑三、什么是Runtime?平时项目中有用过么?
具体应用
四、super的本质
struct objc_super2
SEL
具体实现请参考:
一、讲讲 RunLoop,项目中有用到吗? 一、定时器切换的时候,为了保证定时器的准确性,须要添加runLoop 二、在聊天界面,咱们须要持续的把聊天信息存到数据库中,这个时候须要开启一个保活线程,在这个线程中处理
二、runloop内部实现逻辑
每次运行RunLoop,线程的RunLoop会自动处理以前未处理的消息,并通知相关的观察者。具体顺序
三、RunLoop与线程
四、timer 与 runloop 的关系?
解决定时器在滚动视图上面失效问题NSTimer添加到两种RunLoop中
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
复制代码
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
复制代码
五、RunLoop有几种状态
kCFRunLoopEntry = (1UL << 0), // 即将进入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理Source
kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),// 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7),// 即将退出RunLoop
复制代码
**六、RunLoop的mode的做用 **
RunLoop的mode的做用 系统注册了5中mode
kCFRunLoopDefaultMode //App的默认Mode,一般主线程是在这个Mode下运行
UITrackingRunLoopMode //界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其余 Mode 影响
UIInitializationRunLoopMode // 在刚启动 App 时第进入的第一个 Mode,启动完成后就再也不使用
GSEventReceiveRunLoopMode // 接受系统事件的内部 Mode,一般用不到
kCFRunLoopCommonModes //这是一个占位用的Mode,不是一种真正的Mode
复制代码
可是咱们只能使用两种mode
kCFRunLoopDefaultMode //App的默认Mode,一般主线程是在这个Mode下运行
UITrackingRunLoopMode //界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其余 Mode 影响
复制代码
具体实现请参考:七、RunLoop实现原理
一、你理解的多线程? 二、iOS的多线程方案有哪几种?你更倾向于哪种? 三、你在项目中用过 GCD 吗? 四、GCD 的队列类型 五、说一下 OperationQueue 和 GCD 的区别,以及各自的优点 六、线程安全的处理手段有哪些? 使用线程锁
七、线程通信 线程间通讯的体现
一、NSThread 能够先将本身的当前线程对象注册到某个全局的对象中去,这样相互之间就能够获取对方的线程对象,而后就可使用下面的方法进行线程间的通讯了,因为主线程比较特殊,因此框架直接提供了在主线程执行的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
复制代码
二、GCD
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});
复制代码
一、使用CADisplayLink、NSTimer有什么注意点? CADisplayLink、NSTimer会对target产生强引用,若是target又对它们产生强引用,那么就会引起循环引用
二、介绍下内存的几大区域
三、讲一下你对 iOS 内存管理的理解 在iOS中,使用引用计数来管理OC对象的内存
内存管理的经验总结
能够经过如下私有函数来查看自动释放池的状况 extern void _objc_autoreleasePoolPrint(void);
四、ARC 都帮咱们作了什么 LLVM + Runtime
五、weak指针的实现原理 runtime维护了一个weak表,用于存储指向某个对象的全部weak指针。weak表实际上是一个hash(哈希)表,key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组
六、autorelease对象在什么时机会被调用release
autoreleased 对象是在 runloop 的即将进入休眠时进行释放的
七、方法里有局部对象, 出了方法后会当即释放吗 在ARC状况下会当即释放 在MRC状况下,对象是在 runloop 的即将进入休眠时进行释放的
文章中能够提炼出来的题目太多了,我这里也就简单的总结几道题,想要了解具体实现请到个人github中找到相关文章进行阅读。欢迎点赞哦,若是里面有什么我理解的不太正确,欢迎提出,咱们相互印证