MessageThrottle 修改后崩溃修复
在之前的文章MessageThrottle 结合业务需求的修改 中, 我介绍了 MessageThrottle 的两种节流以及一个防抖模式, 并添加了第四种均匀调用
模式, 但是在实际大规模使用时却遇到了崩溃(每天百万分之一左右).
崩溃原因
上传野指针崩溃如图
崩溃代码发生在
1 2 3 4 5 6 7 8 9
| @interface MTRule () <NSSecureCoding>
@property (nonatomic) NSTimeInterval lastTimeRequest; @property (nonatomic) NSInvocation *lastInvocation; @property (nonatomic) SEL aliasSelector; @property (nonatomic, readwrite, getter=isActive) BOOL active; @property (nonatomic, readwrite) id alwaysInvokeBlock; @property (nonatomic, readwrite) dispatch_queue_t messageQueue; @end
|
在多线程中, lastInvocation 被释放的主要原因是 MTRule.target
(即限频的对象)被释放, 然后 target
的 Invocation 随之释放, 再计时结束准备调用 Invocation 时候发生野指针崩溃.
修复思路
正好 MTRule.target
定义如下:
1 2 3 4
| /** target, 可以为实例,类,元类(可以使用 mt_metaClass 函数获取元类) */ @property (nonatomic, weak, readonly) id target;
|
是个弱引用, 当 target 释放的时候, target
指针会被置为 nil, 因此只需要在崩溃代码之前判定下 target
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| dispatch_async(rule.messageQueue, ^{ if (rule.target == nil) { return; } __strong __typeof__(rule.target) strongTarget = rule.target; if (rule.lastInvocation) { rule.lastInvocation = nil; if (rule.discardMessageBlock != nil) { rule.discardMessageBlock(rule.aliasSelector); } } invocation.selector = rule.aliasSelector; [invocation retainArguments]; rule.lastInvocation = invocation; });
|
后续版本就基本修复了这个崩溃.