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

MTEngine.defaultEngine.classHooked 只有add,没有remove操作,和神策集成发生崩溃 #16

Open
ZhangTonghai opened this issue Dec 2, 2020 · 5 comments
Assignees

Comments

@ZhangTonghai
Copy link

我的工程中集成了神策统计,神策在处理统一的点击事件捕获是,使用了和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;
        }
    }
@yulingtianxia yulingtianxia self-assigned this Dec 2, 2020
@yulingtianxia
Copy link
Owner

yulingtianxia commented Dec 2, 2020

@ZhangTonghai 没太看懂 crash 的原因,是 EditViewController_8_XXXX 这个类已经不存在了么?麻烦能提供个能复现的 demo 么?

@ZhangTonghai
Copy link
Author

手误关闭了issue,叹气!!

@ZhangTonghai ZhangTonghai reopened this Dec 2, 2020
@ZhangTonghai
Copy link
Author

Demo

复现步骤:
1、运行后点击第一个页面红色cell,进入第二个vc
2、返回第一个vc并重复步骤1
3、重复步骤2 ,出现崩溃,如下图
azF3YEtRXUTcgLC

神策业务的大致流程是这样的:

- (void)sensorsdata_setDelegate:(id <UICollectionViewDelegate>)delegate(UIScrollView+AutoTrack.m)

+ (void)hookDidSelectMethodWithDelegate:(id)delegate(SADelegateProxy.m)

+ (NSString *)generateSensorsClassName:(id)obj(SADelegateProxy.m)

...

希望提供的资料有用。

@ZhangTonghai
Copy link
Author

补充:
在设置collectionView的delegate之前调用mt_limitSelector则不会有问题,所以我还没断定问题在这边还是神策方面,忘指点。

@yulingtianxia
Copy link
Owner

yulingtianxia commented Dec 4, 2020

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 了。其实神策反复创建有计数功能的子类并逐个销毁的做法有一定风险,如果换成用属性等来计数可能会更好。

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

No branches or pull requests

2 participants