MessageThrottle 修改后崩溃修复

MessageThrottle 修改后崩溃修复

在之前的文章MessageThrottle 结合业务需求的修改 中, 我介绍了 MessageThrottle 的两种节流以及一个防抖模式, 并添加了第四种均匀调用模式, 但是在实际大规模使用时却遇到了崩溃(每天百万分之一左右).

崩溃原因

上传野指针崩溃如图

崩溃代码发生在

1
2
3
4
5
6
7
8
9
@interface MTRule () <NSSecureCoding>

@property (nonatomic) NSTimeInterval lastTimeRequest;
@property (nonatomic) NSInvocation *lastInvocation; // 纪录的 invocation 对象被释放导致的野指针
@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, ^{
// invocation 偶现崩溃, 原因是 rule.target 在其他线程被释放
// 如果此时的确被释放, 直接返回, 否则用临时变量强引用下
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; // 设置为需要延时执行
});

后续版本就基本修复了这个崩溃.

-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道