当前位置: 首页 > news >正文

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

在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

相关文章:

  • 如何将Vue项目部署至 nginx
  • Trae智能协作AI编程工具IDE:如何在MacBook Pro下载、安装和配置使用Trae?
  • DeepSeek 与大数据治理:AI 赋能数据管理的未来
  • W3C标准和ES规范之一文通
  • FPGA开发,使用Deepseek V3还是R1(3):系统级与RTL级
  • SpringBoot 端口配置
  • nlp第七节——文本匹配任务
  • 下载 MindSpore 配置 PyTorch环境
  • 使用消息队列怎样防止消息重复?
  • 苹果廉价机型 iPhone 16e 影像系统深度解析
  • Rust ~ Dyn Error
  • C语言:质因数分解
  • SpringCloud基础学习
  • C++11特性(笔记二lambda,function)
  • 《解锁万相2.1大模型:开启视频创作新世界》:此文为AI自动生成
  • 聊一聊 IM 如何优化监控
  • spring boot打包插件的问题
  • 计算机毕业设计SpringBoot+Vue.js医院资源管理系统(源码+文档+PPT+讲解)
  • 计算机毕业设计SpringBoot+Vue.js文档管理系统(源码+文档+PPT+讲解)
  • Linux常见基本指令(二)
  • 俄媒:俄乌代表团抵达谈判会场
  • 普京调整俄陆军高层人事任命
  • 新华时评:让医德医风建设为健康中国护航
  • 沧州低空经济起飞:飞行汽车开启千亿赛道,通用机场布局文旅体验
  • 微软将裁员3%,减少管理层
  • 台行政机构网站删除“汉人”改为“其余人口”,国台办回应