概念
封装:提供可以调用的接口,隐藏具体实现方案
继承:子类拥有父类的成员变量和方法,可以较大程度减少代码的重复率
多态:父类指针指向子类对象(实例对象调用的方法会寻找到真实的类进行调用)
应用
封装一个常用的UITableView为例子
-
普通的tableView复用代码
WCBaseTableViewCell
@implementation WCBaseTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self prepareUI];
}
return self;
}
- (void)prepareUI {
self.backgroundColor = [UIColor colorWithRed:random() % 255 / 255.0 green:random() % 255 / 255.0 blue:random() % 255 / 255.0 alpha:1.0];
}
@end
ViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ID = @"WCBaseTableViewCell";
WCBaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[WCBaseTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
效果
-
UITableViewCell的初步封装
从上面的tableViewCell创建的过程来看(如下)
static NSString *ID = @"WCBaseTableViewCell";
WCBaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[WCBaseTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
既然每次实例化一个cell对象都需要写这么一大段代码,那么是否可以把这些代码复用起来(如果WCBaseTableViewCell在多个控制器中使用到,那么就会使得控制器会有很多重复的代码),于是,我们想到了类方法来进行对象的实例化,方法如下:
WCBaseTableViewCell.h
@interface WCBaseTableViewCell : UITableViewCell
+ (instancetype)wc_baseTableViewCellWithTableView:(UITableView *)tableView;
@end
WCBaseTableViewCell.m
@implementation WCBaseTableViewCell
+ (instancetype)wc_baseTableViewCellWithTableView:(UITableView *)tableView {
static NSString *ID = @"WCBaseTableViewCell";
WCBaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[WCBaseTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self prepareUI];
}
return self;
}
- (void)prepareUI {
self.backgroundColor = [UIColor colorWithRed:random() % 255 / 255.0 green:random() % 255 / 255.0 blue:random() % 255 / 255.0 alpha:1.0];
}
@end
- ViewController中的使用*
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
WCBaseTableViewCell *cell = [WCBaseTableViewCell wc_baseTableViewCellWithTableView:tableView];
return cell;
}
总结
以上的普通的tableView复用代码和UITableViewCell的初步封装结果是一致的,使用了工厂方法,将一部分在控制器中常使用的代码放到了cell里面,使得控制器的代码更加易读和简洁,而WCBaseTableViewCell类中的这一部分代码,提供了接口供外部使用声明变量,这,就是封装。
-
UITableViewCell的二次封装(进阶-多态和继承)
从以上的方法来看,是将原本应该在VC中的cell的实例化的代码封装到了cell里作为类方法来使用,这解决了以下问题:在不同地方用到相同的cell的时候需要写一大段的代码进行变量的声明。但是!这同样会有重复代码的问题,比如,不同的cell,一样会需要写一大段类似的类方法进行声明,如下
WCBaseTableViewCell
@implementation WCFirstTableViewCell
+ (instancetype)wc_firstTableViewCellWithTableView:(UITableView *)tableView {
static NSString *ID = @"WCFirstTableViewCell";
WCFirstTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[WCFirstTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self prepareUI];
}
return self;
}
- (void)prepareUI {
}
@end
@implementation WCSecondTableViewCell
+ (instancetype)wc_secondTableViewCellWithTableView:(UITableView *)tableView {
static NSString *ID = @"WCSecondTableViewCell";
WCSecondTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[WCSecondTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self prepareUI];
}
return self;
}
- (void)prepareUI {
}
@end
这是不是又碰到了和上面一样的问题-类似的代码需要重复的写,那么,有没有方法可以避免问题的产生呢?!当然有,这个时候,我们的继承,就发挥出了独特的作用。我们可以设计一个基类(基类拥有一个公共的类方法),让其他的子类继承这个基类,这样就可以避免重复写类似的代码。那么,唯一要解决的两个:
- 基类类方法里的cell标识符如何根据不同的子类声明不同
- 基类类方法的init类如何显示为字类
以上两个问题刚好可以用多态来解决!
WCBaseTableViewCell.m
@implementation WCBaseTableViewCell
+ (instancetype)wc_baseTableViewCellWithTableView:(UITableView *)tableView {
NSString *ID = NSStringFromClass(self.class);
WCBaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[self.class alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self prepareUI];
}
return self;
}
- (void)prepareUI {
}
@end
WCBaseTableViewCell.h
@interface WCBaseTableViewCell : UITableViewCell
+ (instancetype)wc_baseTableViewCellWithTableView:(UITableView *)tableView;
- (void)prepareUI;
@end
这样,一个基类就完成了,实战如下
声明一个继承这个基类WCBaseTableViewCell的类
WCThirdTableViewCell.h
#import "WCBaseTableViewCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface WCThirdTableViewCell : WCBaseTableViewCell
@end
NS_ASSUME_NONNULL_END
WCThirdTableViewCell.m
@implementation WCThirdTableViewCell
- (void)prepareUI {
[super prepareUI];
self.backgroundColor = [UIColor colorWithRed:random() % 255 / 255.0 green:random() % 255 / 255.0 blue:random() % 255 / 255.0 alpha:1.0];
}
@end
在VC中调用如下
ViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
WCThirdTableViewCell *cell = [WCThirdTableViewCell wc_baseTableViewCellWithTableView:tableView];
return cell;
}
效果如下
后续如有要的新的cell,只需要继承WCBaseTableViewCell,然后重写prepareUI方法即可。
此方式的swift版本在后续更新