非作者原著 来自摘抄
参考文献
_objc_msgForward 函数是做什么的 直接调用它将会发生什么
_objc_msgForward是IMP类型 用于消息转发 当向一个对象发送一条消息 但它
并没有实现的时候 _objc_msgForward会尝试做消息转发
objc_msgSend 在"消息转发"中的作用 首先在Class中的缓存查找IMP(没缓存则
初始化缓存) 如果没有找到, 则向父类的Class查找 如果一直查找到根类仍没有
实现 则用_objc_msgForward函数指针代替IMP 最后 执行这个IMP
1. 调用"resolveInstanceMethod:" 方法(或resolveClassMethod:) 允许用户
在此时为该Class动态添加实现 如果有实现了 则调用并返回YES 那么重现开始
objc_msgSend流程 这一次对象会响应这个选择器 一般是因为它已经调用过
class_addMethod 如果仍没有实现 继续下面的动作
2. 调用"forwardingTargetForSelector:" 方法 尝试找到一个能响应该消息的
对象 如果获取到 则直接把消息转发给它 返回非nil对象 否则返回nil 继续下面
的动作 注意 "这里不要返回self 否则会形成死循环"
3. 调用"methodSignatureForSelector: "方法 尝试获取一个方法签名 如果获取
不到 则直接调用doesNotRecognizeSelecor 抛出异常 如果能获取 则返回非
nil 创建一个NSInvocation并传给forwardInvocation
4. 调用"forwardInvocation:" 方法 将第三步获取到的方法前面包装成
Invocation传入 如何处理就在这里面了 并返回非nil
5. 调用"doesNotRecognizeSelector" 默认的实现是抛出异常 如果第三步没能获
取一个方法签名 执行该步骤
_objc_msgForward隶属 C 语言,有三个参数
一旦调用_objc_msgForward 将跳过查找IMP的过程 直接触发"消息转发"
如果调用了_objc_msgForward 即使这个对象确实已经实现了这个方法 它也会告诉objc_msgSend
我没有在这个对象里面找到这个方法的实现
runtime如何实现weak变量的自动置nil 不理解
能否向编译后得到的类中增加实例变量 能否向运行时创建的类中添加实例变量
不能向编译后得到的类中增加实例变量
能向运行时创建的类中添加实例变量
- 因为编译后的类已经注册在runtime中 类结构中的objc_ivar_list 实例变量的
链表和 instance_size 实例变量的内存大小已经确定 同时runtime会调用
class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak引用
所以 不能向存在的类中添加实例变量
- 运行时创建的类是可以添加实例变量 调用class_addIvar函数 但是得在调用
objc_allocateClassPair之后 objc_registerClassPair之前 原理同上
runloop和线程有什么关系
RunLoop 是线程的基础架构部分 每个线程 包括程序的主线程都有与之相应的
run loop对象
主线程的run loop默认是启动的
其他线程的run loop默认是没有启动的
runloop 的mode作用是什么
mode 主要是用来指定时间在运行循环中的优先级的 分为
- NSDefaultRunLoopMode 默认 空闲状态
- UITrackingRunLoopMode ScrollView 滑动时
- UIInitializationRunLoopMode 启动时
- NSRunLoopCommonModes (kCFRunLoopCommonModes) mode集合
以 +scheduledTimerWithTimeInterval... 的方式触发的timer 在滑动列表时 timer会暂停回调 为什么 如何解决
- RunLoop 只能运行在一种mode下 如果要换mode 当前的loop也需要停下重新启动
新的 利用这个机制 ScrollView 滚动过程中NSDefaultRunLoopMode的mode会切换
到UITrackingRunLoopMode来保证ScrollView的流畅滑动 只能在
NSDefaultRunLoopMode模式下处理的事件会影响ScrollView的滑动
- 如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefault
Mode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而
导致NSTimer将不再被调度
//将timer添加到NSDefaultRunLoopMode中
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
//然后再添加到NSRunLoopCommonModes里
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
猜想runLoop内部是如何实现的
一个线程一次只能执行一个任务 执行完成后线程就会退出
//
// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)
// https://github.com/ChenYilong
int main(int argc, char * argv[]) {
//程序一直运行状态
while (AppIsRunning) {
//睡眠状态,等待唤醒事件
id whoWakesMe = SleepForWakingUp();
//得到唤醒事件
id event = GetEvent(whoWakesMe);
//开始处理事件
HandleEvent(event);
}
return 0;
}
objc使用什么机制管理对象内存
通过retainCount 的机制来决定对象是否需要释放 每次RunLoop的时候 都会检查
对象的retainCount 如果retainCount为0 说明该对象没有地方需要继续使用了 可
以释放了
ARC 通过什么方式帮助开发者管理内存
在编译期和运行期两部分共同帮助开发者管理内存
在编译器 ARC用的是更底层的C接口实现retain/release/autorelease 性能更好
同时对同一上下文的同一对象的成对retain/release 操作进行优化
不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
分两种情况 手动干预是否时机 系统自动去释放
1. 手动干预释放时机 --- 指定autoreleasepool 就是所谓的 当前作用域大括号
结束时释放
2. 系统自动去释放 --- 不手动指定autoreleasepool
Autorelease对象出了作用域之后 会被添加到最近一次创建的自动释放池中 并会在当前的runloop迭代结束时释放
[站外图片上传中...(image-14b985-1547623556024)]
在一个完整的运行循环结束之前 会倾倒释放池中的对象 会被销毁
运行循环检测到事件并启动后 就会创建自动释放池
@autoreleasepool 当自动释放池被销毁或者耗尽时,会向自动释放池中的所有对象
发送 release 消息,释放自动释放池中的所有对象。
如果在一个vc的viewDidLoad中创建一个 Autorelease对象,那么该对象会在
viewDidAppear 方法执行前就被销毁了。
BAD_ACCESS 在什么情况下出现
访问了悬垂指针 比如对一个已经释放的对象执行了release 访问已经释放对象的
成员变量或者发消息 死循环
苹果如何实现autoreleasepool的
autoreleasepool 以一个队列数组的形式实现 主要通过下列三个函数完成
- objc_autoreleasepoolPush
- objc_autoreleasepoolPop
- objc_autorelease
[站外图片上传中...(image-210195-1547623556024)]
使用block时什么情况下回发生引用循环 如何解决
一个对象中强引用了block 在block中又强引用了该对象 就会发生循环引用
解决办法就是将该对象使用_weak或者_block修饰符修饰后再在block中使用
- id weak weakSelf = self 或者 weak __typeof(& *self)weakSelf = self该方法可以设置宏
- id _block weakSelf = self;
或者将其中一方强制置空 xxx = nil
在block 内如何修改block外部变量
Block 不允许修改外部变量的值
这里所说的外部变量的值 指的是栈中指针的内存地址 __block所起的到作用就是只
要观察到该变量被block所持有 就将"外部变量"在栈中的内存地址放到了堆中 进而
在block内部也可以修改外部变量的值
Block不允许修改外部变量的值 这里所说的外部变量的值 指的是栈中指针的内存地址 栈区是红灯区 堆区才是绿灯区
使用系统的某些block api (如UIView的block版本写动画时) 是否考虑引用循环问题
系统的某些block api中 UIView的block版本写动画时不需要考虑 但也有一些api
需要考虑
单向的强引用 没有问题
[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.someProperty = xyz; }];
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification) {
self.someProperty = xyz; }];
强引用
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );
__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf dismissModalViewControllerAnimated:YES];
}];
GCD队列
- 串行队列 Serial Dispatch Queue
- 并行队列 Concurrent Dispatch Queue
dispatch_barrier_async 作用
必须搭配自定义并发队列 dispatch_queue_t 使用 不能使用dispatch_get_global_queue 否则 dispatch_barrier_async的作用会和dispatch_async 的作用一模一样
苹果为什么废弃 dispatch_get_current_queue
dispatch_get_current_queue 容易造成死锁
addObserver:forKeyPath:options:context 各个参数的作用分别是什么 observer中需要实现哪个方法才能获得KVO回调
// 添加键值观察
/*
1 观察者,负责处理监听事件的对象
2 观察的属性
3 观察的选项
4 上下文
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
observer中需要实现的方法
```source-objc
// 所有的 kvo 监听到事件,都会调用此方法
/*
1\. 观察的属性
2\. 观察的对象
3\. change 属性变化字典(新/旧)
4\. 上下文,与监听的时候传递的一致
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
如何手动触发一个value的KVO
键值观察通知依赖于 NSObject 的两个方法: willChangeValueForKey: 和
didChangevlueForKey:
KVC的keyPath中的集合运算符如何使用?
1. 必须用在集合对象上或普通对象的集合属性上
2. 简单集合运算符有@avg, @count , @max , @min ,@sum,
3. 格式 @"@sum.age"或 @"集合属性[.@max.age](mailto:.@max.age)"
KVC和KVO的keyPath一定是属性么?
KVC 支持实例变量,KVO 只能手动支持手动设定实例变量的KVO实现监听
50. 如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?
请参考:
apple用什么方式实现对一个对象的KVO
当你观察一个对象时,一个新的类会被动态创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。重写的 setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象:值的更改。最后通过 isa 混写(isa-swizzling) 把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例
手动触发”的使用场景是什么?一般我们只在希望能控制“回调的调用时机”时才会这么做。
而“回调的调用时机”就是在你调用 didChangeValueForKey: 方法时
IBOutlet连出来的视图属性为什么可以被设置成weak?
因为既然有外链那么视图在xib或者stotyboard肯定存在 视图已经对它有一个强引用
网友评论