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

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


http://www.kler.cn/a/569738.html

相关文章:

  • Android Hilt 高级用法
  • 系统架构设计师—计算机基础篇—进度管理
  • 决策树 vs 神经网络:何时使用?
  • Linux-基本指令2
  • 2024蓝桥杯省赛真题-封闭图形个数
  • 青少年编程与数学 02-010 C++程序设计基础 14课题、控制结构
  • PhotoShop2022进行布尔运算--学习日志
  • 网络安全词汇
  • 【新手向】从零开始学习Java(Day17)重写、重载与多态
  • java容器 LIst、set、Map
  • SpringCloud常用企业级别框架整合--上篇
  • Notepad++下载地址【亲测好用】
  • 【0005】Python变量详解
  • 25西医研究生复试面试问题汇总 西医专业知识问题很全! 西医试全流程攻略 西医考研复试调剂真题汇总
  • 【子网掩码计算器:Python + Tkinter 实现】
  • 机器学习数学基础:35.效度
  • Unix Domain Socket和eventfd
  • Minio搭建并在SpringBoot中使用完成用户头像的上传
  • 【机器学习】Logistic回归#1基于Scikit-Learn的简单Logistic回归
  • Element Plus中el-tree点击的节点字体变色加粗