最近项目已到尾声了。对于这个只是换汤不换药的新项目来说,开始接手的时候是有些抵触情绪的。经过熟悉了几个月,出于对职业的责任心,开始一直在公司的iOS讨论组里面分享知识,鼓励大家一起去改变这一切。希望能把共同认为是好的方式带进来。下面是最近进行代码重构的一些心得。
删掉死代码
诸如以下这些:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
使用Category
我们平时写UITableView是这样的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
//do something here
return cell;
}
重构后:
.h文件
@interface UITableView (dequeue)
- (UITableViewCell *)dequeueCell;
@end
.m文件
@implementation UITableView (dequeue)
- (UITableViewCell *)dequeueCell
{
static NSString *cellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
return cell;
}
@end
//使用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueCell];
//do something here
return cell;
}
使用NSDictionary映射字符串等
重构前:
switch (expression) {
case constant1:
return @"result1";
case constant2:
return @"result2";
...
case constantN:
return @"resultN";
default:
return @"default";
}
重构后:
static NSDictionary *resultMap;
if (!resultMap) {
resultMap = @{@(constant1) : @"result1",
@(constant2) : @"result2",
...
@(constantN) : @"resultN"};
}
return resultMap[expression] ?: @"default";
是不是逻辑上变得更清晰了呢?
Objective-C 动态方法
`NSSelectorFromString` 与 `[NSObject performSelector:]`
一般我们是这样写
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
[self func0];
} else if (indexPath.row == 1) {
[self func1];
} else if (indexPath.row == N) {
[self funcN];
} else {
[self doSomething];
}
}
重构后
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SEL funcSEL = NSSelectorFromString([NSString stringWithFormat:@"func%d", indexPath.row]);
if ([self respondsToSelector:funcSEL]) {
[self performSelector:funcSEL];
} else {
[self dosomething];
}
}
加上 前一点 用NSDictionary 来映射字符串,是不是又多了一分想象力呢?
PS:基于以上两点,如果UITableView展示一些客户端固有的属性,可以将数据与点击的响应函数组织成一个字典,放在dataSource里面。
delegate 与 block
接口使用delegate好还是block好呢?个人选择如下:
对于那些block的生命周期里不会出现循环引用的优先选择block。否则选择delegate。
比如,`[NSArray enumerateObjectsUsingBlock:]`和UITableView的delegate两者。`[NSArray enumerateObjectsUsingBlock:]`中,即使有循环引用存在,也将在block在使用完毕后释放。而UITableView中,如果用block,出现循环引用需要自己打破。
基于以上选择规则,UIAlertView跟UISheetView中使用block也是可以的。重构之。
其它一些规则:
杜绝魔数。
使用NS_ENUM来枚举有规则的数。
尽量把变量的声明、定义放在最接近使用的地方。
使用最新的OC语法。
尽量精简方法。