iOS 使用消息转发机制实现多代理功能

devtools/2025/3/6 19:10:28/

在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

http://www.ppmy.cn/devtools/165061.html

相关文章

电脑技巧:硬件检测工具 HWiNFO 8.16版本更新功能介绍

目录 一、版本8.16更新说明 二、安装说明 三、使用说明 HWiNFO是一个专业的系统信息检测工具&#xff0c;支持最新的技术和标准&#xff0c;可检查计算机硬件的所有信息。HWiNFO 主要可以显示出处理器、主板及芯片组、PCMCIA接口、BIOS版本、内存等信息&#xff0c;另外HWiN…

Kotlin语言特性(一):空安全、扩展函数与协程

Kotlin语言特性&#xff08;一&#xff09;&#xff1a;空安全、扩展函数与协程 一、引言 Kotlin作为Android官方推荐的开发语言&#xff0c;相比Java具有诸多现代化特性。本文将重点介绍Kotlin三个最具特色的语言特性&#xff1a;空安全、扩展函数和协程&#xff0c;并结合A…

DeepSeek集成到VScode工具,让编程更高效

DeepSeek与VScode的强强联合&#xff0c;为编程效率树立了新标杆。 DeepSeek&#xff0c;一款卓越的代码搜索引擎&#xff0c;以其精准的索引和高速的检索能力&#xff0c;助力开发者在浩瀚的代码海洋中迅速定位关键信息。 集成至VScode后&#xff0c;开发者无需离开熟悉的编辑…

物联网小范围高精度GPS使用

在园区内实现小范围高精度GPS&#xff08;全球定位系统&#xff09;定位&#xff0c;通常需要结合多种技术来弥补传统GPS在精度和覆盖范围上的不足。以下是实现小范围高精度GPS定位的解决方案&#xff0c;包括技术选择、系统设计和应用场景。 一、技术选择 在园区内实现高精度…

第一篇:Python基础入门

一、Python解释器安装全平台指南 1. Windows系统安装&#xff08;以Python 3.13.2为例&#xff09; 步骤详解&#xff1a; ​下载安装包​ 访问Python官网 → 选择"Windows" → 下载64位安装包&#xff08;如python-3.13.2-amd64.exe&#xff09; ​关键安装选项​…

直接法估计相机位姿

引入 在前面的文章&#xff1a;运动跟踪——Lucas-Kanade光流中&#xff0c;我们了解到特征点法存在一些缺陷&#xff0c;并且用光流法追踪像素点的运动来替代特征点法进行特征点匹配的过程来解决这些缺陷。而这篇文章要介绍的直接法则是通过计算特征点在下一时刻图像中的位置…

P10904 [蓝桥杯 2024 省 C] 挖矿

P10904 [蓝桥杯 2024 省 C] 挖矿 题目描述 小蓝正在数轴上挖矿&#xff0c;数轴上一共有 n n n 个矿洞&#xff0c;第 i i i 个矿洞的坐标为 a i a_i ai​。小蓝从 0 0 0 出发&#xff0c;每次可以向左或向右移动 1 1 1 的距离&#xff0c;当路过一个矿洞时&#xff0c;…

前端八股——JS+ES6

前端八股&#xff1a;JSES6 说明&#xff1a;个人总结&#xff0c;用于个人复习回顾&#xff0c;将持续改正创作&#xff0c;已在语雀公开&#xff0c;欢迎评论改正。