Git Product home page Git Product logo

messagethrottle's Issues

跟想象中不同啊

习惯了 dispatch 那种方式。。。
这种用法有点繁琐,不过你写得很好啊,谢谢你

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;
        }
    }

applyRule: 判断逻辑疑问?

我在阅读源码的时候,有一个疑问。具体的位置是 MTEngineapplyRule: 的实现;
具体的代码如下

- (BOOL)applyRule:(MTRule *)rule
{
    pthread_mutex_lock(&mutex);
    __block BOOL shouldApply = YES;
    if (mt_checkRuleValid(rule)) {
        [self.rules enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, MTRule * _Nonnull obj, BOOL * _Nonnull stop) {
            if (rule.selector == obj.selector
                && mt_object_isClass(rule.target)
                && mt_object_isClass(obj.target)) {
                Class clsA = rule.target;
                Class clsB = obj.target;
                shouldApply = !([clsA isSubclassOfClass:clsB] || [clsB isSubclassOfClass:clsA]);
                *stop = shouldApply;
                NSCAssert(NO, @"Error: %@ already apply rule in %@. A message can only have one rule per class hierarchy.", NSStringFromSelector(obj.selector), NSStringFromClass(clsB));
            }
        }];
        
        if (shouldApply) {
            self.rules[mt_methodDescription(rule.target, rule.selector)] = rule;
            mt_overrideMethod(rule.target, rule.selector);
        }
    }
    pthread_mutex_unlock(&mutex);
    return shouldApply;
}

如果 mt_checkRuleValid(rule) 返回 NO ,最后的方法返回值就是 YES

请问为何限制同一个target的多个Rule

你好,我看到在应用Rule的函数里,target == rule.target限制了同一个target的多个Rule

- (BOOL)applyRule:(MTRule *)rule
{
    pthread_mutex_lock(&mutex);
    MTDealloc *mtDealloc = [rule mt_deallocObject];
    [mtDealloc lock];
    __block BOOL shouldApply = YES;
    if (mt_checkRuleValid(rule)) {
        for (id target in [[self.targetSELs keyEnumerator] allObjects]) {
            if (target == rule.target) {
                shouldApply = NO;
                continue;
            }
            NSMutableSet *selectors = [self.targetSELs objectForKey:target];
            for (NSString *selectorName in selectors) {
                if (sel_isEqual(rule.selector, NSSelectorFromString(selectorName))
                    && mt_object_isClass(rule.target)
                    && mt_object_isClass(target)) {
                    Class clsA = rule.target;
                    Class clsB = target;
                    shouldApply = !([clsA isSubclassOfClass:clsB] || [clsB isSubclassOfClass:clsA]) && shouldApply;
                    NSLog(@"Sorry: %@ already apply rule in %@. A message can only have one rule per class hierarchy.", selectorName, NSStringFromClass(clsB));
                }
            }
        }
        shouldApply = shouldApply && mt_overrideMethod(rule);
        if (shouldApply) {
            [self addSelector:rule.selector onTarget:rule.target];
            rule.active = YES;
        }
    }
    else {
        shouldApply = NO;
    }
    [mtDealloc unlock];
    pthread_mutex_unlock(&mutex);
    return shouldApply;
}

导致我没法给同一个target的不同方法设置规则,请问是出于什么考虑?

MTRule *debounceRule = [[MTRule alloc] initWithTarget:self selector:@selector(updateDebounceString:) durationThreshold:2];
debounceRule.mode = MTPerformModeDebounce;
[debounceRule apply];
NSLog(@"%@", [self mt_allRules]);

MTRule *throttleRule = [[MTRule alloc] initWithTarget:self selector:@selector(updateThrottlingString:) durationThreshold:2];
throttleRule.mode = MTPerformModeLast;
[throttleRule apply];

NSLog(@"%@", [self mt_allRules]);

谢谢

只在iOS16的模拟器上crash

0 CoreFoundation 0x00007ff8004278cb __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007ff80004dba3 objc_exception_throw + 48
2 CoreFoundation 0x00007ff800436ad8 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 0x000000010076ff6a mt_executeOrigForwardInvocation + 650
4 0x000000010076fabf mt_forwardInvocation + 735
5 CoreFoundation 0x00007ff80042bb28 forwarding + 814
6 CoreFoundation 0x00007ff80042e088 _CF_forwarding_prep_0 + 120

崩掉了!

Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 CoreFoundation 0x0000000183d30bc8 -[__NSCFString getCString:maxLength:encoding:] + 4
1 Foundation 0x00000001847c1584 NSSelectorFromString + 96
2 xxxx 0x0000000102fe1c04 mt_forwardInvocation + 252932 (MessageThrottle.m:823)
3 CoreFoundation 0x0000000183e342d4 forwarding + 624
4 CoreFoundation 0x0000000183d1a41c CF_forwarding_prep_0 + 92
5 CoreFoundation 0x0000000183e36580 invoking
+ 144
6 CoreFoundation 0x0000000183d15748 -[NSInvocation invoke] + 284
7 CoreFoundation 0x0000000183d1a56c -[NSInvocation invokeWithTarget:] + 60
8 xxxx 0x0000000102fe1f40 mt_forwardInvocation + 253760 (MessageThrottle.m:0)
9 CoreFoundation 0x0000000183e342d4 forwarding + 624
10 CoreFoundation 0x0000000183d1a41c CF_forwarding_prep_0 + 92
11 CoreFoundation 0x0000000183e36580 invoking
+ 144
12 CoreFoundation 0x0000000183d15748 -[NSInvocation invoke] + 284
13 CoreFoundation 0x0000000183d1a56c -[NSInvocation invokeWithTarget:] + 60
14 xxxx 0x0000000102fe1f40 mt_forwardInvocation + 253760 (MessageThrottle.m:0)
15 CoreFoundation 0x0000000183e342d4 forwarding + 624
16 CoreFoundation 0x0000000183d1a41c CF_forwarding_prep_0 + 92
17 CoreFoundation 0x0000000183e36580 invoking
+ 144
18 CoreFoundation 0x0000000183d15748 -[NSInvocation invoke] + 284
19 CoreFoundation 0x0000000183d1a56c -[NSInvocation invokeWithTarget:] + 60
20 xxxx 0x0000000102fe1f40 mt_forwardInvocation + 253760 (MessageThrottle.m:0)
21 CoreFoundation 0x0000000183e342d4 forwarding + 624
22 CoreFoundation 0x0000000183d1a41c CF_forwarding_prep_0 + 92
23 CoreFoundation 0x0000000183e36580 invoking
+ 144
24 CoreFoundation 0x0000000183d15748 -[NSInvocation invoke] + 284
25 CoreFoundation 0x0000000183d1a56c -[NSInvocation invokeWithTarget:] + 60
26 xxxx 0x0000000102fe1f40 mt_forwardInvocation + 253760 (MessageThrottle.m:0)
27 CoreFoundation 0x0000000183e342d4 forwarding + 624
28 CoreFoundation 0x0000000183d1a41c CF_forwarding_prep_0 + 92
29 CoreFoundation 0x0000000183e36580 invoking
+ 144
30 CoreFoundation 0x0000000183d15748 -[NSInvocation invoke] + 284
31 CoreFoundation 0x0000000183d1a56c -[NSInvocation invokeWithTarget:] + 60

调用如下:

[self.collectionView mt_limitSelector:@selector(beginHeaderRefresh) oncePerDuration:5 usingMode:MTPerformModeFirstly];

每次 viewWillAppear 的时候调通 [ self.collectionView beginHeaderRefresh]

MTPerformModeLast下多线程野指针

bug

见截图,多线程下lastInvocation在不同线程被修改,导致野指针crash。
需要保证messageQueue和mt_handleInvocation所在线程一致才能避免。

A message can only have one rule per class hierarchy.

如果在需要节流的对象 -init 中进行 rule 的申请, 有一定几率会出现这个断言, 但又不必现, 所以想问一下, 如果在需要节流的对象中管理 rule, 怎么样的方式比较合适?

- (instancetype)init
{
    self = [super init];
    if (self) {
        MTRule *rule = [[MTRule alloc] initWithTarget:self selector:@selector(updateProgress:) durationThreshold:0.3];
        rule.mode = MTPerformModeFirstly;
        [MTEngine.defaultEngine applyRule:rule];
    }
    return self;
}

Bug收集平台获取的崩溃信息

Crashed: Thread
0  libsystem_kernel.dylib         0x180f1d2e0 __pthread_kill + 8
1  libsystem_pthread.dylib        0x1810be288 pthread_kill$VARIANT$mp + 376
2  libsystem_c.dylib              0x180e8bd0c abort + 140
3  libsystem_malloc.dylib         0x180f55838 szone_size + 634
4  CoreFoundation                 0x181362c54 -[__NSArrayM insertObject:atIndex:] + 380
5  CoreFoundation                 0x1813aca8c -[NSInvocation retainArguments] + 212
6  MessageThrottle                0x1025e85a0 mt_forwardInvocation + 1916
7  CoreFoundation                 0x18149c2d4 ___forwarding___ + 624
8  CoreFoundation                 0x18138241c _CF_forwarding_prep_0 + 92
9  AllInOne                       0x102adab84 -[CoreData_3_$Lambda$3 lWithId:] + 84
10 ··· ···

AllInOne是我项目中的内容,我把不相关的堆栈信息去掉了。

从第6行开始调用了MessageThrottlemt_forwardInvocation->mt_handleInvocation->retainArguments,接着可能是数组越界还是插入空值的崩溃?并不太清楚崩溃的原因,大神有空看下,可能与库有关。

MTPerformModeLast

MTPerformModeLast 所存储的lastTimeRequest不应该保存now+rule.durationThreshold的时间吗 不然怎么保证的请求的是最后一次

阅读源码有处不理解的

[self.rules enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, MTRule * _Nonnull obj, BOOL * _Nonnull stop) {
            if (rule.selector == obj.selector
                && mt_object_isClass(rule.target)
                && mt_object_isClass(obj.target)) {
                Class clsA = rule.target;
                Class clsB = obj.target;
                shouldApply = !([clsA isSubclassOfClass:clsB] || [clsB isSubclassOfClass:clsA]);
                *stop = shouldApply;
                NSCAssert(NO, @"Error: %@ already apply rule in %@. A message can only have one rule per class hierarchy.", NSStringFromSelector(obj.selector), NSStringFromClass(clsB));
            }
        }];

在遍历中如果 clsA 和 clsB 不是继承关系的话,shouldApply = YES,为什么要打印 Error信息呢?

也许可以换一种调用方式

为了更方便易用,可不可以在方法内部添加过滤限制,

- (IBAction)tapFoo:(UIButton *)sender {
    [self mt_limitSelector:_cmd oncePerDuration:0.5];
}

稍微改了下代码,应该可以实现的,就是第一次有问题,😄
纯粹个人习惯

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

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.