iOS|iOS 正确创建使用数据源或代理方法。

简要 我们平时做SDK封装,可能会想用UITableView那样的设置数据源或代理的方式来做封装。

self.tableView.dataSource = self; self.tableView.delegate = self;

这样做很好,我们可以自己封装一个。
需求点 昨天,我需要封装一个滚动号码球视图,像老虎机那种,它的子视图是由外部自定义的,我的想法是像tabelView自定义cell一样自定义子视图。
如何做 以我的SDK为例,我们先创建好数据源方法或代理方法
@protocol BTScrollBallDataSource @optional /// 要显示多少组号码 /// @param scrollBall 试图 - (NSInteger)numberOfSectionsInScrollBall:(BTScrollBallView *)scrollBall; @end@interface BTScrollBallView : UIView /// 数据源方法 @property (weak,nonatomic) id dataSource; @end@implementation BTScrollBallView - (instancetype)init{ self = [super init]; if (self) { self.backgroundColor = UIColor.greenColor; [self createLayers]; //在此处调用方法创建视图 } return self; }- (void)createLayers{ // 这里通过代理方法获取数据源个数 NSInteger sections = 0; if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInScrollBall:)]) { sections = [self.dataSource numberOfSectionsInScrollBall:self]; } } @end

认真观察一下 到这里你可能会认为已经可以了,可以获取到数据了。
但其实这里有一个很大的问题:这里根本无法拿到数据源对象,这里的self.dataSource是为nil的。
在控制器UIViewController中使用一下看看:
@interface UIViewController() @property (strong,nonatomic) BTScrollBallView *scrollBall; @end@implementation UIViewController- (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.scrollBall]; }// MARK: - MKScrollBallDataSource - (NSInteger)numberOfSectionsInScrollBall:(BTScrollBallView *)scrollBall{ return self.datas.count; }- (BTScrollBallView *)scrollBall{ if (!_scrollBall) { _scrollBall = [[BTScrollBallView alloc]init]; _scrollBall.dataSource = self; } return _scrollBall; } @end

【iOS|iOS 正确创建使用数据源或代理方法。】可以看到,我们调用完_scrollBall = [[BTScrollBallView alloc]init]; 之后才调用设置数据源_scrollBall.dataSource = self; ,这个时候BTScrollBallView视图中init方法想通过数据源获取数据怎么可能有值呢?
那怎么办,难道要在初始化的时候把数据源dataSourcedelegate传进去吗?这限制太死了,非常不合理,我们看看更好的方式。
正确的姿势 我们可以等视图已经完成添加到父视图之后再获取代理delegatedataSource,此时的视图已经初始化创建完成了。这时再去获取数据源代理就能获取成功。
看看UIView的生命周期:

iOS|iOS 正确创建使用数据源或代理方法。
文章图片
UIView的生命周期
在UIView的生命周期有一条:didMoveToSuperView方法,这个方法就是视图被添加到父视图完成后走的方法。
到这里就很明了了,我们在这个方法调用获取代理delgate或dataSource就没有问题了。
- (void)didMoveToSuperview{ [super didMoveToSuperview]; [self createLayers]; }- (void)createLayers{ NSInteger sections = 0; if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInScrollBall:)]) { sections = [self.dataSource numberOfSectionsInScrollBall:self]; } }

谢谢查阅!
详细代码:
BTScrollBallView
参考:
UIView生命周期

    推荐阅读