MessageThrottle
MessageThrottle copied to clipboard
MTEngine.defaultEngine.classHooked 只有add,没有remove操作,和神策集成发生崩溃
我的工程中集成了神策统计,神策在处理统一的点击事件捕获是,使用了和MessageThrottle类似的生成一个形如EditViewController_6_XXXX 虚拟子类的操作,此处的数字会递增,反复进入同一个页面,可能会生成EditViewController_8_XXXX、EditViewController_9_XXXX 这种情况。
在 mt_overrideMethod 这个方法中,[MTEngine.defaultEngine.classHooked addObject:cls]添加的cls实际是神策统计生成的虚拟子类。 由于没有remove的操作,当下一次限流方法再次执行时,下面的代码在进行isSubclassOfClass判断是会发生崩溃,因为classHooked里的存放是EditViewController_8_XXXX,而传入的是EditViewController_9_XXXX。
// check if subclass has hooked!
for (Class clsHooked in MTEngine.defaultEngine.classHooked) {
if (clsHooked != cls && [clsHooked isSubclassOfClass:cls]) {
NSLog(@"Sorry: %@ used to be applied, can't apply it's super class %@!", NSStringFromClass(cls), NSStringFromClass(cls));
return NO;
}
}
@ZhangTonghai 没太看懂 crash 的原因,是 EditViewController_8_XXXX
这个类已经不存在了么?麻烦能提供个能复现的 demo 么?
手误关闭了issue,叹气!!
复现步骤:
1、运行后点击第一个页面红色cell,进入第二个vc
2、返回第一个vc并重复步骤1
3、重复步骤2 ,出现崩溃,如下图
神策业务的大致流程是这样的:
- (void)sensorsdata_setDelegate:(id <UICollectionViewDelegate>)delegate(UIScrollView+AutoTrack.m)
+ (void)hookDidSelectMethodWithDelegate:(id)delegate(SADelegateProxy.m)
+ (NSString *)generateSensorsClassName:(id)obj(SADelegateProxy.m)
...
希望提供的资料有用。
补充: 在设置collectionView的delegate之前调用mt_limitSelector则不会有问题,所以我还没断定问题在这边还是神策方面,忘指点。
Crash 原因是 MessageThrottle 这边直接使用了神策动态创建的子类,然后下次神策创建新的子类时,会销毁旧创建的子类,导致下次 MessageThrottle 访问到不存在的类而 crash。神策使用 objc_disposeClassPair()
函数销毁创建的类时,一旦这个类存在实例,或存在其子类的实例,那么就会发生问题。因为神策在销毁子类之前并没有按照苹果官网文档的要求进行检查(是否存在实例或子类),而是暴力销毁,所以发生在神策之后的 Hook 操作都会受此影响,此问题需要神策来修复:
/**
* Destroy a class and its associated metaclass.
*
* @param cls The class to be destroyed. It must have been allocated with
* \c objc_allocateClassPair
*
* @warning Do not call if instances of this class or a subclass exist.
*/
OBJC_EXPORT void
objc_disposeClassPair(Class _Nonnull cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
『在设置collectionView的delegate之前调用mt_limitSelector则不会有问题』,是因为 MessageThrottle 先创建了子类并持有,神策后创建的子类并销毁,这样就影响不到 MessageThrottle 了。其实神策反复创建有计数功能的子类并逐个销毁的做法有一定风险,如果换成用属性等来计数可能会更好。