iOS swizzle 系统的代理方法

需求背景

这次有个需求,就是滑动tableView的时候需要记录一些事件,停止的时候也要记录一些事件,而项目里面的tableView很多,而且以后加的tableView也需要监听这些事件,所以需要统一处理一下,现在的方案就是通过交换tableView的相关代理方法来做统一的处理

实现思路

  • swizzle的事件都为scrollView的事件,从scrollView这个类下手
  • swizzle scrollView的setDelegate:方法
  • 在自定义的setDelegate:方法中swizzle相应的代理方法
  • 在自定义的swizzle方法中做相关的处理

最初实现(有问题版本)

思路很简单,接下来看看实现

  • 添加用于交换方法的函数
/**
 * class : 替换方法的类
 * originalSelector : 原始方法SEL
 * swizzledSelector : 用于交换的SEL
 * noneSelector : 原方法SEL对应IMP不存在的时候用的SEL
**/
void classInstanceMethodSwizzle(Class class, SEL originalSelector, SEL swizzledSelector,SEL noneSelector)
{
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    Method noneMethod = class_getInstanceMethod(class, noneSelector);
    //已经交换过了
    if (method_getImplementation(originalMethod) == method_getImplementation(swizzledMethod)) {
        return;
    }
    
    BOOL originMethodExist = originalMethod != nil;
    //源方法不存在就直接添加noneSEL对应的IMP
    if (!originMethodExist&&noneMethod) {
            class_addMethod(class, originalSelector, method_getImplementation(noneMethod), method_getTypeEncoding(noneMethod));
        return;
    }
    
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        //当前类不存在originalSelector而父类存在的时候didAddMethod为YES,避免影响父类的相关方法功能走replaceMethod
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}
  • 添加scrollView的分类,swizzle setDelegate:方法

#import "LHSwizzle.h"

@implementation UIScrollView (LHExtension)
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        classInstanceMethodSwizzle(self.class,
                                   @selector(setDelegate:),
                                   @selector(cs_setDelegate:),
                                   nil);
    });
}

  • 在自定义的setDelegate:方法中swizzle相应的代理方法
- (void)cs_setDelegate:(id<UIScrollViewDelegate>)delegate
{
    if ([self isKindOfClass:UITableView.class]) {
        if (delegate) {
            classInstanceMethodSwizzle([delegate class],
                                       @selector(scrollViewWillBeginDragging:),
                                       @selector(swizzling_tableViewWillBeginDragging:),
                                       @selector(none_tableViewWillBeginDragging:));
            
            classInstanceMethodSwizzle([delegate class],
                                       @selector(scrollViewDidEndDecelerating:),
                                       @selector(swizzling_tableViewDidEndDecelerating:),
                                       @selector(none_tableViewDidEndDecelerating:));
            
            
            classInstanceMethodSwizzle([delegate class],
                                       @selector(scrollViewDidEndDragging:willDecelerate:),
                                       @selector(swizzling_tableViewDidEndDragging:willDecelerate:),
                                       @selector(none_tableViewDidEndDragging:willDecelerate:));
            
            
            classInstanceMethodSwizzle([delegate class],
                                       @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:),
                                       @selector(swizzling_tableView:didEndDisplayingCell:forRowAtIndexPath:),
                                       @selector(none_tableView:willDisplayCell:forRowAtIndexPath:));
            
            
        }
        
    }else if ([self isKindOfClass:UICollectionView.class]) {
        if (delegate) {
            classInstanceMethodSwizzle([delegate class],
                                       @selector(scrollViewWillBeginDragging:),
                                       @selector(swizzling_collectionViewWillBeginDragging:),
                                       @selector(none_collectionViewWillBeginDragging:));
            
            
            
            
            classInstanceMethodSwizzle([delegate class],
                                       @selector(scrollViewDidEndDecelerating:),
                                       @selector(swizzling_collectionViewDidEndDecelerating:),
                                       @selector(none_collectionViewDidEndDecelerating:));
            
            
            
            classInstanceMethodSwizzle([delegate class],
                                       @selector(scrollViewDidEndDragging:willDecelerate:),
                                       @selector(swizzling_collectionViewDidEndDragging:willDecelerate:),
                                       @selector(none_collectionViewDidEndDragging:willDecelerate:));
            
            
            
            
            
            classInstanceMethodSwizzle([delegate class],
                                       @selector(collectionView:willDisplayCell:forItemAtIndexPath:),
                                       @selector(none_collectionView:willDisplayCell:forItemAtIndexPath:),
                                       @selector(none_collectionView:willDisplayCell:forItemAtIndexPath:));
            
            
        }
    }
    
    [self cs_setDelegate:delegate];
}
  • 在自定义的swizzle方法中做相关的处理
#import "NSObject+LHScrollSwizzleMethod.h"

@implementation NSObject (LHScrollSwizzleMethod)

- (void)swizzling_collectionViewWillBeginDragging:(UICollectionView *)collectionView
{
    [self none_collectionViewWillBeginDragging:collectionView];
    [self swizzling_collectionViewWillBeginDragging:collectionView];
}

- (void)none_collectionViewWillBeginDragging:(UICollectionView *)collectionView
{
    [GBEventRecord event:@"scrollBegin"];
}


- (void)swizzling_collectionViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate
{
    [self none_collectionViewDidEndDecelerating:collectionView];
    [self swizzling_collectionViewDidEndDragging:collectionView willDecelerate:decelerate];
    
}

- (void)none_collectionViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate
{
    [self none_collectionViewDidEndDecelerating:collectionView];
}

- (void)swizzling_collectionViewDidEndDecelerating:(UICollectionView *)collectionView
{
    [self none_collectionViewDidEndDecelerating:collectionView];
    [self swizzling_collectionViewDidEndDecelerating:collectionView];
}

- (void)none_collectionViewDidEndDecelerating:(UICollectionView *)collectionView
{
    [GBEvent event:@"scrollEnd"];
}

- (void)swizzling_collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    [self none_collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath];
    [self swizzling_collectionView:collectionView
                   willDisplayCell:cell
                forItemAtIndexPath:indexPath];
}

- (void)none_collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    [GBEvent event:@"cellWillShow"];
}

- (void)swizzling_tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self none_tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
    [self swizzling_tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:indexPath];
}

- (void)none_tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self none_tableViewWillBeginDragging:tableView];
}

- (void)swizzling_tableViewWillBeginDragging:(UITableView *)tableView
{
    [self none_tableViewWillBeginDragging:tableView];
    [self swizzling_collectionViewWillBeginDragging:tableView];
}

- (void)none_tableViewWillBeginDragging:(UITableView *)tableView
{
    [GBEvent event:@"scrollBegin"];
}

- (void)swizzling_tableViewDidEndDragging:(UITableView *)tableView willDecelerate:(BOOL)decelerate
{
    [self none_tableViewDidEndDecelerating:tableView];
    [self swizzling_tableViewDidEndDragging:tableView willDecelerate:decelerate];
}

- (void)none_tableViewDidEndDragging:(UITableView *)tableView willDecelerate:(BOOL)decelerate
{
    [GBEvent event:@"scrollEnd"];
}

- (void)swizzling_tableViewDidEndDecelerating:(UITableView *)tableView
{
    [self none_tableViewDidEndDecelerating:tableView];
    [self swizzling_collectionViewDidEndDecelerating:tableView];
}

- (void)none_tableViewDidEndDecelerating:(UITableView *)tableView
{
    [GBEvent event:@"scrollEnd"];
}

@end

遇到的问题

  • 相同的scrollView 的 setDelegate:方法会调用多次,对应的delegate会执行多次方法交换,调用两次setDelegate:后相当于没有交换
  • 自定义的tableView和UITableView是父子关系,会存在子类父类重复交换的问题

最终实现

分析

  • 需要解决相同类多次交换的问题;可以通过给setDelegate:进来的每个类添加带类名的SEL,让这个SEL指向对应的swizzle方法的IMP,对这个SEL进行交换,然后再次交换的时候判断是否存在这个SEL就行
  • 需要解决子类父类重复交换的问题;可以判定originSEL和swizzleSEL的IMP是否相等判断
  • 在前两条的情况下,子类可能不去做方法交换的逻辑,所以存在子类可以调用了自定义处理逻辑,但是调用不到父类方法源方法的问题;可以通过superClass向上查找父类的swizzle方法进行调用

代码

  • UIScrollView分类的改动


#define GET_CLASS_CUSTOM_SEL(sel,class)  NSSelectorFromString([NSString stringWithFormat:@"%@_%@",NSStringFromClass(class),NSStringFromSelector(sel)])

- (BOOL)isContainSel:(SEL)sel inClass:(Class)class
{
    unsigned int count;
    Method *methodList = class_copyMethodList(class,&count);
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        NSString *tempMethodString = [NSString stringWithUTF8String:sel_getName(method_getName(method))];
        if ([tempMethodString isEqualToString:NSStringFromSelector(sel)]) {
            free(methodList);
            return YES;
        }
    }
    free(methodList);
    return NO;
}

- (void)cs_setDelegate:(id<UIScrollViewDelegate>)delegate
{
    if ([self isKindOfClass:UITableView.class]) {
        if (delegate) {
            BOOL hasSwizzled = NO;
            SEL delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(scrollViewWillBeginDragging:),[delegate class]);
            if (class_getMethodImplementation(delegate.class, @selector(scrollViewWillBeginDragging:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_tableViewWillBeginDragging:))) {
                hasSwizzled = YES;
            }
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_tableViewWillBeginDragging:)))
                                types:"v@:@"];
                classInstanceMethodSwizzle([delegate class],
                                           @selector(scrollViewWillBeginDragging:),
                                           delegateSEL,
                                           @selector(none_tableViewWillBeginDragging:));
            }
            hasSwizzled = NO;
            if (class_getMethodImplementation(delegate.class, @selector(scrollViewDidEndDecelerating:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_tableViewDidEndDecelerating:))) {
                hasSwizzled = YES;
            }
            delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(scrollViewDidEndDecelerating:),[delegate class]);
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_tableViewDidEndDecelerating:)))
                                types:"v@:@"];
                
                classInstanceMethodSwizzle([delegate class],
                                           @selector(scrollViewDidEndDecelerating:),
                                           delegateSEL,
                                           @selector(none_tableViewDidEndDecelerating:));
            }
            
            hasSwizzled = NO;
            if (class_getMethodImplementation(delegate.class, @selector(scrollViewDidEndDragging:willDecelerate:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_tableViewDidEndDragging:willDecelerate:))) {
                hasSwizzled = YES;
            }
            delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(scrollViewDidEndDragging:willDecelerate:),[delegate class]);
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_tableViewDidEndDragging:willDecelerate:)))
                                types:"v@:@"];
                
                classInstanceMethodSwizzle([delegate class],
                                           @selector(scrollViewDidEndDragging:willDecelerate:),
                                           delegateSEL,
                                           @selector(none_tableViewDidEndDragging:willDecelerate:));
            }
            hasSwizzled = NO;
            if (class_getMethodImplementation(delegate.class, @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_tableView:didEndDisplayingCell:forRowAtIndexPath:))) {
                hasSwizzled = YES;
            }
            delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(tableView:didEndDisplayingCell:forRowAtIndexPath:),[delegate class]);
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_tableView:didEndDisplayingCell:forRowAtIndexPath:)))
                                types:"v@:@"];
                
                classInstanceMethodSwizzle([delegate class],
                                           @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:),
                                           delegateSEL,
                                           @selector(none_tableView:willDisplayCell:forRowAtIndexPath:));
            }
            
        }
        
    }else if ([self isKindOfClass:UICollectionView.class]) {
        if (delegate) {
            BOOL hasSwizzled = NO;
            SEL delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(scrollViewWillBeginDragging:),[delegate class]);
            if (class_getMethodImplementation(delegate.class, @selector(scrollViewWillBeginDragging:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_collectionViewWillBeginDragging:))) {
                hasSwizzled = YES;
            }
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_collectionViewWillBeginDragging:)))
                                types:"v@:@"];
                classInstanceMethodSwizzle([delegate class],
                                           @selector(scrollViewWillBeginDragging:),
                                           delegateSEL,
                                           @selector(none_collectionViewWillBeginDragging:));
            }
            
            hasSwizzled = NO;
            if (class_getMethodImplementation(delegate.class, @selector(scrollViewDidEndDecelerating:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_collectionViewDidEndDecelerating:))) {
                hasSwizzled = YES;
            }
            delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(scrollViewDidEndDecelerating:),[delegate class]);
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_collectionViewDidEndDecelerating:)))
                                types:"v@:@"];
                
                classInstanceMethodSwizzle([delegate class],
                                           @selector(scrollViewDidEndDecelerating:),
                                           delegateSEL,
                                           @selector(none_collectionViewDidEndDecelerating:));
            }
            
            hasSwizzled = NO;
            if (class_getMethodImplementation(delegate.class, @selector(scrollViewDidEndDragging:willDecelerate:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_collectionViewDidEndDragging:willDecelerate:))) {
                hasSwizzled = YES;
            }
            delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(scrollViewDidEndDragging:willDecelerate:),[delegate class]);
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_collectionViewDidEndDragging:willDecelerate:)))
                                types:"v@:@i"];
                classInstanceMethodSwizzle([delegate class],
                                           @selector(scrollViewDidEndDragging:willDecelerate:),
                                           delegateSEL,
                                           @selector(none_collectionViewDidEndDragging:willDecelerate:));
                
                
            }
            
            hasSwizzled = NO;
            if (class_getMethodImplementation(delegate.class, @selector(collectionView:willDisplayCell:forItemAtIndexPath:)) ==
                class_getMethodImplementation(delegate.class, @selector(swizzling_collectionView:willDisplayCell:forItemAtIndexPath:))) {
                hasSwizzled = YES;
            }
            delegateSEL = GET_CLASS_CUSTOM_SEL(@selector(collectionView:willDisplayCell:forItemAtIndexPath:),[delegate class]);
            if (!hasSwizzled&&![self isContainSel:delegateSEL inClass:[delegate class]]) {
                [self class_addMethod:[delegate class]
                             selector:delegateSEL
                                  imp:method_getImplementation(class_getInstanceMethod([delegate class],@selector(swizzling_collectionView:willDisplayCell:forItemAtIndexPath:)))
                                types:"v@:@@@"];
                classInstanceMethodSwizzle([delegate class],
                                           @selector(collectionView:willDisplayCell:forItemAtIndexPath:),
                                           delegateSEL,
                                           @selector(none_collectionView:willDisplayCell:forItemAtIndexPath:));
            }
            
        }
    }
    
    [self cs_setDelegate:delegate];
}
  • NSObject分类的改动
@implementation NSObject (LHScrollSwizzleMethod)

- (id)lh_performSelector:(SEL)selector withObjects:(NSArray *)objects
{
    // 方法签名(方法的描述)
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
    if (signature == nil) {
        return nil;
        //可以抛出异常也可以不操作。
    }
    
    // NSInvocation : 利用一个NSInvocation对象包装一次方法调用(方法调用者、方法名、方法参数、方法返回值)
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = selector;
    
    // 设置参数
    NSInteger paramsCount = signature.numberOfArguments - 2; // 除self、_cmd以外的参数个数
    paramsCount = MIN(paramsCount, objects.count);
    for (NSInteger i = 0; i < paramsCount; i++) {
        id object = objects[i];
        if ([object isKindOfClass:[NSNull class]]) continue;
        [invocation setArgument:&object atIndex:i + 2];
    }
    
    // 调用方法
    [invocation invoke];
    
    // 获取返回值
    id returnValue = nil;
    if (signature.methodReturnLength) { // 有返回值类型,才去获得返回值
        [invocation getReturnValue:&returnValue];
    }
    
    return returnValue;
}

- (void)callOriginSEL:(SEL)sel params:(NSArray *)arr
{
    Class curClass = self.class;
    while (curClass) {
        SEL delegateSEL = GET_CLASS_CUSTOM_SEL(sel,curClass);
        if ([self respondsToSelector:delegateSEL]) {
            [self snk_performSelector:delegateSEL withObjects:arr];
            break;
        }
        curClass = [curClass superclass];
    }
}

- (void)swizzling_collectionViewWillBeginDragging:(UICollectionView *)collectionView
{
    [self none_collectionViewWillBeginDragging:collectionView];
    [self callOriginSEL:@selector(scrollViewWillBeginDragging:) params:@[collectionView]];
}

- (void)swizzling_collectionViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate
{
    [self none_collectionViewDidEndDecelerating:collectionView];
    [self callOriginSEL:@selector(scrollViewDidEndDragging:willDecelerate:) params:@[collectionView,@(decelerate)]];
}

- (void)swizzling_collectionViewDidEndDecelerating:(UICollectionView *)collectionView
{
    [self none_collectionViewDidEndDecelerating:collectionView];
    [self callOriginSEL:@selector(scrollViewDidEndDecelerating:) params:@[collectionView]];
}

- (void)swizzling_collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    [self none_collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath];
    [self callOriginSEL:@selector(collectionView:willDisplayCell:forItemAtIndexPath:) params:@[collectionView,cell,indexPath]];
}


- (void)swizzling_tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self none_tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
    [self callOriginSEL:@selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) params:@[tableView,cell,indexPath]];
}

- (void)swizzling_tableViewWillBeginDragging:(UITableView *)tableView
{
    [self none_tableViewWillBeginDragging:tableView];
    [self callOriginSEL:@selector(scrollViewWillBeginDragging:) params:@[tableView]];
}

- (void)swizzling_tableViewDidEndDragging:(UITableView *)tableView willDecelerate:(BOOL)decelerate
{
    [self none_tableViewDidEndDecelerating:tableView];
    [self callOriginSEL:@selector(scrollViewDidEndDragging:willDecelerate:) params:@[tableView,@(decelerate)]];
}

- (void)swizzling_tableViewDidEndDecelerating:(UITableView *)tableView
{
    [self none_tableViewDidEndDecelerating:tableView];
    [self callOriginSEL:@selector(scrollViewDidEndDecelerating:) params:@[tableView]];
}

- (void)swizzling_collectionViewWillBeginDragging:(UICollectionView *)collectionView
{
    [self none_collectionViewWillBeginDragging:collectionView];
    [self swizzling_collectionViewWillBeginDragging:collectionView];
}

- (void)none_collectionViewWillBeginDragging:(UICollectionView *)collectionView
{
    [GBEventRecord event:@"scrollBegin"];
}

- (void)none_collectionViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate
{
    [self none_collectionViewDidEndDecelerating:collectionView];
}

- (void)none_collectionViewDidEndDecelerating:(UICollectionView *)collectionView
{
    [GBEvent event:@"scrollEnd"];
}

- (void)none_collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    [GBEvent event:@"cellWillShow"];
}

- (void)none_tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self none_tableViewWillBeginDragging:tableView];
}

- (void)none_tableViewWillBeginDragging:(UITableView *)tableView
{
    [GBEvent event:@"scrollBegin"];
}

- (void)none_tableViewDidEndDragging:(UITableView *)tableView willDecelerate:(BOOL)decelerate
{
    [GBEvent event:@"scrollEnd"];
}

- (void)none_tableViewDidEndDecelerating:(UITableView *)tableView
{
    [GBEvent event:@"scrollEnd"];
}

@end
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 转自 iOS 面试常见问题最全梳理 序言 目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了。今年,找...
    ZMJun阅读 788评论 0 11
  • 面向对象的三大特性:封装、继承、多态 OC内存管理 _strong 引用计数器来控制对象的生命周期。 _weak...
    运气不够技术凑阅读 1,082评论 0 10
  • 呀呀 又大一岁啦 谢谢你们呀 在我不知道的情况下给了我惊喜 还有晓芬的打了我4个电话和10几个语音通话 但是由于我...
    Yamo阅读 433评论 0 0
  • 周一早上例行与中医医生的约会,下午三点三十分,地铁2号线西北段开通,距离中医院最近的一个地铁站也将开放。无疑,将会...
    桂之华阅读 225评论 0 2