iOS开发,事半功倍基本心得(二)


3. 常用代码的封装。

有些朋友可能不明白什么时候是需要将一组代码封装到一个Method中,其实最简单的就是当你复制粘贴的时候,你就应该问问自己:"是不是可以把这组代码封装到一个或者多个Methods中,以便重复使用。" 这样主要能让你的Project看上去清楚简洁,而且一定意义上也节省了你项目的大小(虽然代码不会占太大的空间)。就比如我们常用的stringWithFormat或者isEqualToString等等的Methods,在他们背后都是,一大块被封装在这个Method里面的代码。只要我们理解这个Interface Method的功能,背后的代码已经不重要了。举个很简单的例子,在我们3.0的软件中,我们几乎吧所有的用户头像就变成圆形,也许大家都知道这个很简单,只需要两行代码,

    imageView.clipsToBounds = YES;  
    imageView.layer.cornerRadius = image.frame.size.width/2;

而基于有轻度代码洁癖的我,只要是超过一行,而且被反复被使用的代码就应该封装到一个Method中,所有就变成了这样

+ (void)roundImage:(UIImageView *)imageView {  
    imageView.clipsToBounds = YES;
    imageView.layer.cornerRadius = imageView.frame.size.width/2;
}

让我们来看一个比较复杂的例子,在我们的项目中,反复使用了Attributed String, 主要是我们有大量的用户Review, 酒店餐厅景点的简介,我们想控制String的行间距,否则会看起来太拥挤。于是我们把所需要用到的代码都分装在一个Method中:

+ (NSMutableAttributedString *)generateAttributedStringFromString:(NSString *)string  
                                   withFontName:(NSString *)fontName 
                                   fontSize:(float)fontSize 
                                   textAlignment:(NSTextAlignment)alignment 
                                   lineBreakMode:(NSLineBreakMode)lineBreakMode
{
    NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc]   initWithString:string];
    NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
    style.lineSpacing = fontSize / 2;
    style.alignment = alignment;
    style.lineBreakMode = lineBreakMode;
    [mutableAttributedString addAttribute:(NSString*)kCTParagraphStyleAttributeName value:style range:NSMakeRange(0, [mutableAttributedString length])];
    [mutableAttributedString addAttribute:NSFontAttributeName value:[UIFont fontWithName:fontName size:fontSize] range:NSMakeRange(0, [mutableAttributedString length])];

    return mutableAttributedString;
}

这样一来,一个需要7,8行来实现的功能,简单的封装就可以缩减变成1,2行。我简单的在我们的项目里所搜了一下,我们一共有42处Call了这个Method,粗略一算我们至少节约了230多行的代码。

4. 最后就是Comments(注释)的重要性。
其实不同的程序员对Comments有着不同的理解,有些人认为Comments是基本没有意义的,因为他们觉得好的Code应该是很容易被理解的,包括其作用,类别等等都应该一目了然。这个论调确实也有一定的道理,但我不能说100%赞同。我认为Comments的作用不是让其他程序员甚至你自己理解你这段代码的作用究竟是什么,而更多的应该把这段代码的逻辑描述出来。除非是非常简单的一两行的代码,否则都应该有当时的编写的逻辑。这些逻辑在过了几个月甚至一年,哪怕你是编写者,你回头去看,都会需要一段时间才能想起来究竟当时基于什么原因选择了这样做,更不要说其他合作的同事。比如说这段简单关于某些部分Show,某些Hide的代码:

if (myPlaceView.currentTripPlan.isTemporary) {  
   myPlaceView.myPlacesCountLabel.hidden = YES;
   myPlaceView.activityIndicator.hidden = NO;
   [myPlaceView.activityIndicator startAnimating];
}
else  
{
   myPlaceView.activityIndicator.hidden = YES;
   [myPlaceView.activityIndicator stopAnimating];
   myPlaceView.myPlacesCountLabel.hidden = NO;
   myPlaceView.myPlacesCountLabel.text = [myPlaceView.currentTripPlan.trip.placesCount stringValue];
}

如果直接读的话,很难明白为什么Trip plan是temporary就需要吧PlaceCountLabel隐藏和等等东西。如果加上这些注释:

//Since when user creates a new trip plan, we will save a temporary local copy of it  
//so when they come back to splashboard, they will see the new trip plan before the api responses
if (myPlaceView.currentTripPlan.isTemporary) {  
    //in this case, our api is not responded yet
    //we need to show the loader, and hide the place count
    myPlaceView.myPlacesCountLabel.hidden = YES;
    myPlaceView.activityIndicator.hidden = NO;
    [myPlaceView.activityIndicator startAnimating];
}
else  
{
    //if current trip plan is not temporary, that means we get the data from the api
    myPlaceView.activityIndicator.hidden = YES;
    [myPlaceView.activityIndicator stopAnimating];
    myPlaceView.myPlacesCountLabel.hidden = NO;
    myPlaceView.myPlacesCountLabel.text = [myPlaceView.currentTripPlan.trip.placesCount stringValue];
}

看了Comments之后其实是很容易理解的,Temporary是一个新建立的trip plan所拥有的Flag,而Temporary是一个Local Copy而不是从数据库读取到的。当我们获得了数据库数据后,将把所有的信息都Update并且显示。

再比如你的代码中包含了数学计算或者公式等,和一些Hard coded的数字,添加一些注释也是非常有帮助的,否则当你过了一段时间回头来看时会被那些毫无来由的数字弄的晕头转向。