Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tableView使用MessageThrottle崩掉了,在工程中偶现,bugly收集到了一些 #17

Open
ghost opened this issue Jul 14, 2022 · 20 comments

Comments

@ghost
Copy link

ghost commented Jul 14, 2022

libobjc.A.dylib
_objc_setAssociatedObject + 48
1
BuGeElectricContest
-[MTRule deallocObject] (MessageThrottle.m:179)
2
BuGeElectricContest
-[MTRule deallocObject] (MessageThrottle.m:179)
3
BuGeElectricContest
-[MTRule invokingLastInvocation] (MessageThrottle.m:190)
4
libdispatch.dylib
__dispatch_client_callout + 16

13
UIKitCore
_UIApplicationMain + 164
14
BuGeElectricContest
main (main.m:14)
15
libdyld.dylib
_start + 4

image

@ghost
Copy link
Author

ghost commented Jul 14, 2022

image
每次在View init的时候添加了一个间隔一秒刷新一次页面的防抖,使用的版本是1.4.0

@yulingtianxia
Copy link
Owner

请问限频的对象有使用 KVO 么?如果有的话,可以升级到 1.4.1
如果依然没有解决问题,麻烦你可以提供一个可以复现的 demo 么?

@ghost
Copy link
Author

ghost commented Jul 14, 2022

请问限频的对象有使用 KVO 么?如果有的话,可以升级到 1.4.1
如果依然没有解决问题,麻烦你可以提供一个可以复现的 demo 么?

image
限频的对象是一个tableView,没有使用KVO。

@yulingtianxia
Copy link
Owner

有 crash.log 之类的完整日志么

@ghost
Copy link
Author

ghost commented Jul 20, 2022

目前没有crash.log的完整日志,我使用的版本是1.4.1版本,我继续观察,如果有的话会发给你的

@yulingtianxia
Copy link
Owner

好的,感谢

@carbon1985
Copy link

会有可能出现这种可能吗?

在主循环的过程中,对象A被回收,同时之前某个时刻的延迟操作(MTPerformModeLast模式)也在这个循环中执行,这时候invokingLastInvocation执行的时候调用[self deallocObject]去访问被回收的对象A,从而导致崩溃。

static void mt_handleInvocation {
....
case MTPerformModeLast: {
invocation.selector = rule.aliasSelector;
[invocation retainArguments];
dispatch_async(rule.messageQueue, ^{
rule.lastInvocation = invocation;
if (now - rule.lastTimeRequest > rule.durationThreshold) {
rule.lastTimeRequest = now;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(rule.durationThreshold * NSEC_PER_SEC)), rule.messageQueue, ^{
[rule invokingLastInvocation];
});
}
});
break;
}
...
}

==================

  • (void)invokingLastInvocation {
    if (!self.isActive) {
    self.lastInvocation.selector = self.selector;
    }
    // 判断 target 现在的类型和之前 hook 时的类型的子类(或相同)
    // 如果判断成立,则可以正常 invoke;否则说明 isa 指针被其他程序修改过了,需要重新 apply rule,修正 isa。
    MTDealloc *mtDealloc = [self deallocObject];
    Class originalClass = object_getClass(self.target);
    BOOL valid = [originalClass isSubclassOfClass:mtDealloc.cls];
    if (!valid) {
    [self discard];
    [self apply];
    }
    [self.lastInvocation invoke];
    self.lastInvocation = nil;
    }

@yulingtianxia
Copy link
Owner

yulingtianxia commented Jul 23, 2022

会有可能出现这种可能吗?

在主循环的过程中,对象A被回收,同时之前某个时刻的延迟操作(MTPerformModeLast模式)也在这个循环中执行,这时候invokingLastInvocation执行的时候调用[self deallocObject]去访问被回收的对象A,从而导致崩溃。

static void mt_handleInvocation { .... case MTPerformModeLast: { invocation.selector = rule.aliasSelector; [invocation retainArguments]; dispatch_async(rule.messageQueue, ^{ rule.lastInvocation = invocation; if (now - rule.lastTimeRequest > rule.durationThreshold) { rule.lastTimeRequest = now; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(rule.durationThreshold * NSEC_PER_SEC)), rule.messageQueue, ^{ [rule invokingLastInvocation]; }); } }); break; } ... }

==================

  • (void)invokingLastInvocation {
    if (!self.isActive) {
    self.lastInvocation.selector = self.selector;
    }
    // 判断 target 现在的类型和之前 hook 时的类型的子类(或相同)
    // 如果判断成立,则可以正常 invoke;否则说明 isa 指针被其他程序修改过了,需要重新 apply rule,修正 isa。
    MTDealloc *mtDealloc = [self deallocObject];
    Class originalClass = object_getClass(self.target);
    BOOL valid = [originalClass isSubclassOfClass:mtDealloc.cls];
    if (!valid) {
    [self discard];
    [self apply];
    }
    [self.lastInvocation invoke];
    self.lastInvocation = nil;
    }

[invocation retainArguments] 会持有 target 的,应该不会被释放

@yulingtianxia
Copy link
Owner

yulingtianxia commented Jul 23, 2022

是否有其他线程可能会触发释放掉 tableView 呢 @fangYaLei110

@carbon1985
Copy link

从我们的崩溃日志来看,这个对象的确是被释放了,然后invokingLastInvocation才会被调用

@yulingtianxia
Copy link
Owner

虽然我还没复现 target 被释放的场景,但我先加了个保护兜底,可以使用 1.4.2 版本试试 @carbon1985 @fangYaLei110
想问下你们复现的工程里有使用 MRC 或者多线程读写这个对象的场景么?

@carbon1985
Copy link

我们是用arc的,多线程读写应该不会有这个问题吧,我看了一下日志和崩溃堆栈,也都是在主线程上执行的

@yulingtianxia
Copy link
Owner

主要是没有想到 target 被释放的原因,可能是在 retainArguments 方法调用前其他线程写操作导致 target 释放,但我没复现这种 case;我最担心的是 invocation 被释放导致 target 进而被释放,这意味着只针对 target 判空保护是不够的,参数列表也危险了。不过在这个场景下我看都是在主线程(rule.messageQueue)上执行的,应该不存在 invocation 被释放的问题

@ghost
Copy link
Author

ghost commented Jul 29, 2022

主要是没有想到 target 被释放的原因,可能是在 retainArguments 方法调用前其他线程写操作导致 target 释放,但我没复现这种 case;我最担心的是 invocation 被释放导致 target 进而被释放,这意味着只针对 target 判空保护是不够的,参数列表也危险了。不过在这个场景下我看都是在主线程(rule.messageQueue)上执行的,应该不存在 invocation 被释放的问题

我们的工程中好多因为这个问题崩溃的,但是目前也不知道什么原因,我想着使用pod更新到1.4.2,但是发现pod里面并没有搜索到1.4.2版本

@ghost
Copy link
Author

ghost commented Jul 29, 2022

这个崩溃在我们工程中目前存在70多个
image

@ghost
Copy link
Author

ghost commented Jul 29, 2022

image
image
image
我们在初始化的时候进行了MTRule初始化,对mtRule_checkEnterChannel这个方法调用进行了防抖的操作,这个使用方式会存在什么问题吗

@yulingtianxia
Copy link
Owner

image
image
image
我们在初始化的时候进行了MTRule初始化,对mtRule_checkEnterChannel这个方法调用进行了防抖的操作,这个使用方式会存在什么问题吗

这样没问题的

@yulingtianxia
Copy link
Owner

主要是没有想到 target 被释放的原因,可能是在 retainArguments 方法调用前其他线程写操作导致 target 释放,但我没复现这种 case;我最担心的是 invocation 被释放导致 target 进而被释放,这意味着只针对 target 判空保护是不够的,参数列表也危险了。不过在这个场景下我看都是在主线程(rule.messageQueue)上执行的,应该不存在 invocation 被释放的问题

我们的工程中好多因为这个问题崩溃的,但是目前也不知道什么原因,我想着使用pod更新到1.4.2,但是发现pod里面并没有搜索到1.4.2版本

是可以搜到的:https://cocoapods.org/pods/MessageThrottle
应该是你的本地缓存没更新?

@ghost
Copy link
Author

ghost commented Jul 29, 2022

主要是没有想到 target 被释放的原因,可能是在 retainArguments 方法调用前其他线程写操作导致 target 释放,但我没复现这种 case;我最担心的是 invocation 被释放导致 target 进而被释放,这意味着只针对 target 判空保护是不够的,参数列表也危险了。不过在这个场景下我看都是在主线程(rule.messageQueue)上执行的,应该不存在 invocation 被释放的问题

我们的工程中好多因为这个问题崩溃的,但是目前也不知道什么原因,我想着使用pod更新到1.4.2,但是发现pod里面并没有搜索到1.4.2版本

是可以搜到的:https://cocoapods.org/pods/MessageThrottle 应该是你的本地缓存没更新?

好的,非常感谢,已经搜到,我们做灰度测试一下

@yulingtianxia
Copy link
Owner

请问后续还有碰到此问题么 @fangYaLei110

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants