在iOS開發中,我們有時候會用到多代理功能,比如我們列表的埋點事件,需要我們在列表的某個特定的時機進行埋點上報,我們當然可以用最常見的做法,就是設置代理實現代理方法,然后在對應的代理方法里面進行上報,但是這樣做有個問題,就是會做大量重復的工作,我們想要到達的效果是,我們只需要實現業務邏輯,而埋點操作,只需需要我們配置一下數據,就會自動進行,這樣就為我們減少了大量的重復性工作。
下面介紹一下我們實現列表的自定化埋點的思路
我們自定義一個列表類,繼承于系統類,然后該類有一個代理中心,
該代理中心類負責消息轉發,他引用了真正的原始代理,和一個代理對象,該代理對象也實現了列表的代理方法,里面的實現只進行埋點操作。 我們重寫自定義列表類的setDelegate方法,在里面創建代理對象,并將列表的代理設置為代理中心,在代理中心中將消息轉發給代理對象和原始代理, 通過這樣的方式,實現了自動化埋點
代碼
代理中心
//
// LBDelegateCenter.m
// TEXT
//
// Created by mac on 2025/3/2.
// Copyright ? 2025 劉博. All rights reserved.
//#import "LBDelegateCenter.h"@implementation LBDelegateCenter- (instancetype)initWithTarget:(id)target proxy:(id)proxy
{if (self = [super init]) {_target = target ? target : [NSNull null];_proxy = proxy ? proxy : [NSNull null];}return self;
}- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{NSMethodSignature *targetSign = [_target methodSignatureForSelector:sel];if (targetSign) {return targetSign;}NSMethodSignature *proxySign = [_proxy methodSignatureForSelector:sel];if (proxySign) {return proxySign;}return [super methodSignatureForSelector:sel];
}- (void)forwardInvocation:(NSInvocation *)anInvocation
{//AUKLogInfo(@"New proxy = %@, selector=%@", [self.proxy class], NSStringFromSelector([anInvocation selector]));BOOL hit = NO;if ([_target respondsToSelector:[anInvocation selector]]) {hit = YES;[anInvocation invokeWithTarget:_target];}if ([_proxy respondsToSelector:[anInvocation selector]]) {//AUKLogInfo(@"New proxy handle");hit = YES;[anInvocation invokeWithTarget:_proxy];}if (!hit && [super respondsToSelector:[anInvocation selector]]) {[super forwardInvocation:anInvocation];}
}- (BOOL)respondsToSelector:(SEL)aSelector
{if ([_target respondsToSelector:aSelector]) {return YES;}if ([_proxy respondsToSelector:aSelector]) {return YES;}return [super respondsToSelector:aSelector];
}- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{if ([_target conformsToProtocol:aProtocol]) {return YES;}if ([_proxy conformsToProtocol:aProtocol]) {return YES;}return [super conformsToProtocol:aProtocol];
}- (BOOL)isKindOfClass:(Class)aClass
{if ([_target isKindOfClass:aClass]) {return YES;}if ([_proxy isKindOfClass:aClass]) {return YES;}return [super isKindOfClass:aClass];
}- (BOOL)isMemberOfClass:(Class)aClass
{if ([_target isMemberOfClass:aClass]) {return YES;}if ([_proxy isMemberOfClass:aClass]) {return YES;}return [super isMemberOfClass:aClass];
}@end
代理對象
//
// LBScrollViewDelegate.m
// TEXT
//
// Created by mac on 2025/3/2.
// Copyright ? 2025 劉博. All rights reserved.
//#import "LBScrollViewDelegate.h"
#import <UIKit/UIKit.h>
#import "UITableViewCell+Event.h"
#import "UICollectionViewCell+Event.h"@implementation LBScrollViewDelegate// 自動輪播的開始,但是需要重點關注是否可能存在觸發scrollViewDidScroll
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{//執行滾動
}- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{//開始拖動,執行曝光埋點
}//
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{//開始減速, 停止滾動
}//
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{//停止減速
}//
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{if (!decelerate) {//停止滾動}
}//
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{//
}// 10.3.86 切換使用新方法代替點擊捕獲
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];[cell setMonitorSelected:YES];
}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];[cell setMonitorSelected:YES];
}// 結束顯示周期是準確的,但開始顯示可能只顯示一次,可能顯示并不完全,所以暫只開了開始。
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{if ([self needCheckCellIn:tableView isStart:YES]) {}
}- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{if ([self needCheckCellIn:collectionView isStart:YES]) {}
}- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
{if ([self needCheckCellIn:tableView isStart:NO]) {}
}- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{if ([self needCheckCellIn:collectionView isStart:NO]) {}
}- (BOOL)needCheckCellIn:(UIView *)view isStart:(BOOL)start
{return YES;
}@end
自定義列表類
//
// LBCollectionView.m
// TEXT
//
// Created by mac on 2025/3/2.
// Copyright ? 2025 劉博. All rights reserved.
//#import "LBEventCollectionView.h"
#import "LBScrollViewDelegate.h"
#import "LBDelegateCenter.h"@implementation LBEventCollectionView- (void)didMoveToWindow
{[super didMoveToWindow];//執行cell 曝光埋點
}- (void)reloadData
{[super reloadData];[self checkNeedReportLog_auk];}- (void)checkNeedReportLog_auk
{if (!self.window || !self.superview) {return;}// 上報當前visiblecell及其子view的所有埋點,放到下一個runloop,等到cell渲染完成[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(performCommitScroll) object:nil];[self performSelector:@selector(performCommitScroll) withObject:nil afterDelay:0.5];}- (void)performCommitScroll
{//執行曝光埋點
}- (LBScrollViewDelegate *)collectionDelegate_auk
{if (!_collectionDelegate_auk) {_collectionDelegate_auk = [[LBScrollViewDelegate alloc] init];}return _collectionDelegate_auk;
}- (void)setDelegate:(id<UICollectionViewDelegate>)delegate
{if (delegate == nil) {
// self.delegateProxy_auk = nil;[super setDelegate:nil];return;}self.delegateProxy_auk = [[LBDelegateCenter alloc] initWithTarget:self.collectionDelegate_auk proxy:delegate];self.delegateProxy_auk.scrollView = self;[super setDelegate:(id)self.delegateProxy_auk];
}- (NSMutableDictionary *)visibleCellInfos_auk
{if (!_visibleCellInfos_auk) {_visibleCellInfos_auk = [[NSMutableDictionary alloc] init];}return _visibleCellInfos_auk;
}- (NSMutableDictionary *)lastVisibleInfos_auk
{if (!_lastVisibleInfos_auk) {_lastVisibleInfos_auk = [[NSMutableDictionary alloc] init];}return _lastVisibleInfos_auk;
}- (NSHashTable *)validViews_auk
{if (!_validViews_auk) {_validViews_auk = [NSHashTable hashTableWithOptions:NSHashTableWeakMemory];}return _validViews_auk;
}- (void)dealloc
{
// self.delegate = nil;_delegateProxy_auk = nil;_collectionDelegate_auk = nil;
}- (BOOL)supportAspectExposure
{return NO;
}@end