返回

共享密钥,多方共享,iOS实现一对多代理方案揭秘

闲谈

“多线程是苹果爆炸的导火索,而代理就是那枚地雷。”

毫无疑问,代理模式在iOS应用开发中的使用非常广泛。苹果给了我们一个相当简单的解决方案去实现某些有趣的事情,甚至苹果官方也相当鼓励用代理模式去解决很多问题,就好像给一台汽车加装了四轮涡轮增压器和四个尾翼。

当然,这不是一篇文章的核心。我们先来聊聊代理的分类:

  • 一对一代理: 一个代理对象只服务于一个目标对象。这是一个最常见的代理类型。比如说,在iOS系统中,当我们通过UITableView来展示数据时,为了让它显示数据,就必须有一个数据源对象。而这个数据源,在代理模式中,就是目标对象。而我们的UITableView,就是代理对象,因为UITableView会从数据源中去获取数据来展示。

  • 一对多代理: 一个代理对象可以服务于多个目标对象。这是我们今天要介绍的代理类型,而这种代理也可以称之为多路复用代理。

  • 多对一代理: 多个代理对象服务于一个目标对象。这种代理类型使用较少,原因在于它更多用于一种设计模式而不是一种代理。在实际应用中,多对一代理经常被认为是多目标模式或多委托模式。

  • 多对多代理: 多个代理对象服务于多个目标对象。这种代理类型和多对一代理一样,多用于一种设计模式而不是一种代理。

现在,我们已经弄清楚代理的分类,那我们来讲讲如何实现一对多代理。首先,我们先看看一对一代理的实现:

@interface ViewController : UIViewController <UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSMutableArray *dataSource;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化tableView
    self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
    self.tableView.dataSource = self;
    
    // 初始化dataSource
    self.dataSource = [NSMutableArray array];
    
    // 添加数据到dataSource
    [self.dataSource addObject:@"数据1"];
    [self.dataSource addObject:@"数据2"];
    [self.dataSource addObject:@"数据3"];
}

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    cell.textLabel.text = self.dataSource[indexPath.row];
    return cell;
}

@end

这个示例中,ViewController就是代理对象,UITableView就是目标对象。ViewController实现了UITableViewDataSource协议,这使它能够成为UITableView的数据源。然后,我们在viewDidLoad方法中初始化了UITableViewdataSource,并添加了一些数据到dataSource中。最后,我们实现了UITableViewDataSource协议中的两个方法,这两个方法分别是tableView:numberOfRowsInSection:tableView:cellForRowAtIndexPath:,这两个方法返回UITableView中行的数量和每个单元格中的数据。

那我们该如何实现一对多代理呢?只需几步:

  1. 创建一个可变数组来存储代理对象。
  2. 在代理对象的初始化方法中,将代理对象添加到可变数组中。
  3. 在目标对象中,遍历可变数组中的代理对象,并调用它们的代理方法。
@interface Target : NSObject

@property (nonatomic, strong) NSMutableArray *delegates;

- (void)doSomething;

@end

@implementation Target

- (instancetype)init {
    self = [super init];
    if (self) {
        self.delegates = [NSMutableArray array];
    }
    return self;
}

- (void)doSomething {
    for (id delegate in self.delegates) {
        if ([delegate respondsToSelector:@selector(targetDidSomething:)]) {
            [delegate targetDidSomething:self];
        }
    }
}

- (void)addDelegate:(id)delegate {
    [self.delegates addObject:delegate];
}

- (void)removeDelegate:(id)delegate {
    [self.delegates removeObject:delegate];
}

@end

@interface DelegateA : NSObject <TargetDelegate>

@end

@implementation DelegateA

- (void)targetDidSomething:(Target *)target {
    NSLog(@"DelegateA received message from Target");
}

@end

@interface DelegateB : NSObject <TargetDelegate>

@end

@implementation DelegateB

- (void)targetDidSomething:(Target *)target {
    NSLog(@"DelegateB received message from Target");
}

@end

int main() {
    Target *target = [[Target alloc] init];
    DelegateA *delegateA = [[DelegateA alloc] init];
    DelegateB *delegateB = [[DelegateB alloc] init];
    [target addDelegate:delegateA];
    [target addDelegate:delegateB];
    [target doSomething];
    [target removeDelegate:delegateA];
    [target doSomething];
    return 0;
}

在上面的示例中,Target类是目标对象,而DelegateADelegateB类是代理对象。Target类有一个名为delegates的可变数组,用来存储代理对象。Target类还实现了doSomething方法,该方法遍历delegates数组中的代理对象,并调用它们的代理方法。DelegateADelegateB类都实现了TargetDelegate协议,该协议包含一个名为targetDidSomething:的方法。当Target类的doSomething方法被调用时,它会遍历delegates数组中的代理对象,并调用它们的targetDidSomething:方法。这样,DelegateADelegateB类就会收到Target类的消息,并作出相应的反应。

最后,我们再来说说需要注意的地方:

  • 由于数组是强引用,因此,我们需要在合适的地方释放它,以避免内存泄漏。
  • 使用一对多代理时,需要谨慎地管理代理对象的生命周期,以确保它们不会在目标对象之前被释放。

希望本文能帮助您更好地理解一对多代理以及如何实现它。如果您有任何问题或建议,请随时留言。