如何让你的代码写的更优雅?(持续更新)

前言

以前写代码也仅仅是注意C语言一些基本的规范还有驼峰命名法什么的,后来看了一些书籍,也写过不少代码,review过不少别人的代码,所以把自己的一些感悟记一下吧.在本文的最后我会把我们老大写的集团的iOS端编码规范放一下,第一部分的那个新特性的规范的demo就是把我之前的代码改了下,哈哈.

这篇文章会持续更新,这里我会把我文章最后除了附录中的集团的代码规范外自己的东西会写出来.再看这篇文章前推荐看两本书Objective-C高级编程 iOS与OS X多线程和内存管理,Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法,第二本书读完写了篇读书笔记,这是链接

自己的一些编码技巧和代码规范(2017.7.6版)

tableview中cell的复用id

  • 1在需要使用的控制器内这么写,使用static修饰,但这样你如果换了个控制器还得再写一遍很僵硬,不是很推荐.

    static NSString *baseInfomationSubCellID = @"CBaseInfoSubCell";
    
  • 2在cell头文件上声明一个常量,采用extern来修饰,这有个优点,就是当你的cell需要在多个控制器上使用时,会很方便.

      //头文件,你要是使用苹果的extern当然也可以
      extern NSString *const CBASE_INFOMATION_CELLID;
      //实现文件
      NSString *const CBASE_INFOMATION_CELLID = @"CBaseInfomationCell";
    
  • 3在UITableViewCell的基类或者分类中写一个类方法,能够获取cell的类名
    + (NSString *)getCellIDStr
    {
    return NSStringFromClass([self class]);
    }

控件可以这样命名,这样联想起来更加的方便

比如说一个姓名label你可以起名为lb_name,这样当你想调用他的时候是不是更方便呢?公司现在的要求是写成nameLabel的.

条件语句或者参数过多时请换行对齐

        [[KLPrestoClient prestoClient] postBaseInfoMationWithEducation:_postParaFlags.education
                                                         monthlyIncome:_postParaFlags.monthlyIncome
                                                        professionType:_postParaFlags.jobInfo
                                                              marriage:_postParaFlags.marriage
                                                              cityName:_cityNameStr
                                                           cityAddress:_addressStr
                                                              latitude:_latitudeStr
                                                             longitude:_longitudeStr
                                                       isProvidentFund:providentFundModel.selectedIndex
                                                          eMailAddress:_EMailAddrStr
                                                      censusIsLiveCity:censusModel.selectedIndex
                                                                 isHud:YES
                                                               success:^(BaseModel *model) {
                                                                   
            if ([model.status isEqualToString:@"000"]) {
                
                //上传数据成功
                [self.navigationController popViewControllerAnimated:YES];
                
            } else {
                
            }
            
        } failture:^(id error) {
        }];

尽量多的利用参数和返回值,尽量不要使用全局变量

    /**
     对城市按照首字母分组
     */
    - (NSArray *)p_makeCityGroup:(NSArray *)cityArray{
        NSMutableArray *letterDictArray = [NSMutableArray array];
        //创建26个数组用来存储
        for (int i = 0; i < 26; i ++) {
            NSMutableArray *letterCityArray = [NSMutableArray array];
            [letterDictArray addObject:letterCityArray];
        }
        
        for (CCityModel *model in cityArray) {
            
            //将城市首字母转化为asic码
            int asciiCode = [model.cityCode characterAtIndex:3];
            int index = asciiCode - 97;
            [letterDictArray[index] addObject:model];
        }
        return [letterDictArray copy];
    }

在一些需求巧妙地利用完全初始化方法,不要将不需要外部改变的属性readwrite

    /**
     普通model
     */
    - (instancetype)initNormalModelWithTitle:(NSString *)title answer:(NSString *)answer answerArray:(NSArray *)answerArray {

        return [self initBaseInfomationWithTitle:title
                                          answer:answer
                                   selectedIndex:NO_CHOOSE_ANSWER_INDEX
                                           isMap:NO
                                     answerArray:answerArray
                              isTwoChooseBtnCell:NO
                              isEMailAddressCell:NO];
    }

    /**
     带地图的model
     */
    - (instancetype)initMapModelWithTitle:(NSString *)title answer:(NSString *)answer {
        
        return [self initBaseInfomationWithTitle:title
                                          answer:answer
                                   selectedIndex:NO_CHOOSE_ANSWER_INDEX
                                           isMap:YES
                                     answerArray:nil
                              isTwoChooseBtnCell:NO
                              isEMailAddressCell:NO];
    }

    /**
     二选一model
     */
    - (instancetype)initTwoChooseOneModelWithTitle:(NSString *)title answerArray:(NSArray *)answerArray {
        //index传-1表示二选一未选择
        return [self initBaseInfomationWithTitle:title
                                          answer:@""
                                   selectedIndex:NO_CHOOSE_ANSWER_INDEX
                                           isMap:NO
                                     answerArray:answerArray
                              isTwoChooseBtnCell:YES
                              isEMailAddressCell:NO];
    }

    /**
     带邮箱的model
     */
    - (instancetype)initEMailModelWithTitle:(NSString *)title defaultAnswer:(NSString *)defaultAnswer{
        //index传-1表示未输入
        return [self initBaseInfomationWithTitle:title
                                          answer:defaultAnswer
                                   selectedIndex:NO_CHOOSE_ANSWER_INDEX
                                           isMap:NO
                                     answerArray:nil
                              isTwoChooseBtnCell:NO
                              isEMailAddressCell:YES];
    }

    /**
     完全初始化
     */
    - (instancetype)initBaseInfomationWithTitle:(NSString *)title answer:(NSString *)answer selectedIndex:(NSInteger)selectedIndex isMap:(BOOL)isMap answerArray:(NSArray *)answerArray isTwoChooseBtnCell:(BOOL)isTwoChooseBtnCell isEMailAddressCell:(BOOL)isEMailAddressCell{
        if (self = [super init]) {
            _title = title;
            _answer = answer;
            _selectedIndex = selectedIndex;
            _isMap = isMap;
            _answerArray = answerArray;
            _isTwoChooseBtnCell = isTwoChooseBtnCell;
            _isEMailAddressCell = isEMailAddressCell;
        }
        return self;
    }

    @end

附录(集团iOS端代码规范)

Objective-C 编程风格指南

Controller注释、命名示例

  • 1、请在类文件顶部注明作者姓名
  • 2、控件命名请使用 控件作用拼接数据类型 (例如:UIButton => settingButton UIButton => submitButton UILabel => companyLabel )
  • 3、一个方法中代码尽量不要超过20行,如超过请考虑按职责将代码拆分到子方法中,保障主方法流程清晰
  • 4、类、类成员(变量或方法)顶部应添加注释说明清楚类的职责、变量的作用或者方法的提供的功能
  • 5、方法内注释请使用 双斜杠加空格再加说明文案
//
//  JLaunchViewController.m
//  J1000
//
//  Created by 作者姓名 on 17/2/8.
//  Copyright © 2017年 user. All rights reserved.
//

#import "JLaunchViewController.h"

@interface JLaunchViewController ()<UIScrollViewDelegate>

/*
 * 引导页位置指示器
 */
@property (weak, nonatomic) IBOutlet UIPageControl *dotPageControl;

/*
 * 引导图容器
 */
@property (weak, nonatomic) IBOutlet UIScrollView *intrScrollView;

/*
 * 进入按钮
 */
@property (weak, nonatomic) IBOutlet UIButton *nextButton;

@end

@implementation JLaunchViewController{
    /*
     * 引导图数组
     */
    NSMutableArray * _imageArray;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // 初始化界面
    [self initView];
    
    // 数据绑定()
    [self dataBind];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - UI
/*
 * 初始化界面
 */
- (void)initView{
    
    // 设置scrollView属性(隐藏水平滚动条,边框不反弹、整页翻动、关联代理类)
    self.intrScrollView.showsHorizontalScrollIndicator = NO;
    self.intrScrollView.bounces = NO;
    self.intrScrollView.pagingEnabled = YES;
    self.intrScrollView.delegate = self;
    
    // 设置pageControl属性(设置指示圆点默认色值与选中色值、背景颜色、整页翻动后再切换当前位置)
    self.dotPageControl.currentPageIndicatorTintColor = [UIColor blackColor];
    self.dotPageControl.pageIndicatorTintColor = [UIColor whiteColor];
    self.dotPageControl.backgroundColor = [UIColor clearColor];
    self.dotPageControl.defersCurrentPageDisplay = YES;
    
    // 默认隐藏底部按钮
    self.nextButton.hidden = YES;
}

/*
 * 数据绑定
 */
- (void)dataBind{

    // 测试
    _imageArray =  [[NSMutableArray alloc]initWithObjects:@1, @2, @2, @2, @2, nil];
    
    // 设置ScrollView内容的宽度与高度
    self.intrScrollView.contentSize = CGSizeMake(ScreenWidth * _imageArray.count, ScreenHeight);
    
    // 循环添加图片
    for (int i = 0; i< _imageArray.count ; i++) {
        UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(ScreenWidth * i, 0, ScreenWidth, ScreenHeight)];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.backgroundColor = i == 0 ? [UIColor greenColor] : [UIColor orangeColor];
        [self.intrScrollView addSubview:imageView];
    }
    
    // 设置pageControl圆点总数与当前位置
    self.dotPageControl.numberOfPages = _imageArray.count;
    self.dotPageControl.currentPage = 0;
    
}
#pragma mark - target
/*
 * 进入按钮单击事件处理函数
 */
- (IBAction)didClickNextBtn:(id)sender {
    
    if (self.loginSwitchVCBlock) {
        self.loginSwitchVCBlock();
    }
}

#pragma mark - delegate
/*
 * 滚动视图滚动事件处理函数
 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    // 当拖动偏移量超过半屏时切换圆点位置
    self.dotPageControl.currentPage = (int) (scrollView.contentOffset.x + ScreenWidth/2) /ScreenWidth;
    
    // 判断是否隐藏进入按钮
    self.nextButton.hidden = !(self.dotPageControl.currentPage == _imageArray.count - 1);
}

@end

Xcode项目

  • 为避免文件混乱,实际的物理文件应和Xcode项目保持一直。在Xcode中所创建的任何group都应有文件系统中相对应的文件夹。不应仅根据文件类型来进行分组,还需要考虑到其作用。

简写

  • 较长的单词可取单词的头几个字母形成缩写
  • 仅在变量命名,图片命名中使用
名称 缩写
infomation info
message msg
descriptation desc

类的命名

  • 大驼峰式命名:类的命名的时候,每个单词的首字母都采用大写字母。(项目简写+名称+后缀)。
要求 例如
ViewController 使用Controller做后缀 例子: DDFHomeController
View 使用View做后缀 例子: DDFAlertView
UITableCell 使用Cell做后缀 例子: DDFNewsCell
Protocol 使用Delegate或者DataSource作为后缀 例子: UITableViewDelegate
Model 使用model作为后缀 例子: DDFDataModel
单例 manager 例子: DDFUserInfoManager
类的拓展 类名+项目简写+extra 例子: NSString+DDFExtra

宏命名

要求 例如
宏定义方法 小驼峰命名 例子: getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",DDFBaseUrl,url]]
宏定义常量 遵循大驼峰命名(开头以项目简写) 例子:#define DDFWidth self.frame.size.width, KScreenWidth [UIScreen mainScreen].bouds.size.width

变量的命名

  • 用修饰+类型的方式 ,类型简写参考简写。
要求 例如
用修饰+类型的方式 (小驼峰式命名) UIButton *settingButton

property变量

要求 例如
小驼峰式命名 @property (strong, nonatomic) NSSNumber *userId;
NSString 使用copy @property (copy, nonatomic) NSString *userName;
所有属性特性应该显式地列出来 @property (weak, nonatomic) IBOutlet UIView *containerView;

图片命名

  • 可以进行简写 nav,btn,bg,imagev
要求 例如
功能名称_____控件类型 ___自定义参数 share_____btn _weixin

枚举类型

  • 项目简写开头,跟随其后的命名应采用驼峰命名法则,命名应准确表述枚举表示的意义,枚举中各个值都应以定义的枚举类型开头,其后跟随各个枚举值对应的状态、选项或者状态码。
typedef NS_ENUM(NSInteger, DDFRequestState) {
    DDFRequestStateInactive,
    DDFRequestStateLoading
}; 

方法命名

  • 使用小驼峰法,规范的方法名应该看起来像一个完整的句子,读过便知函数作用。

  • 在(-/+ )符号之后应加上一个空格

- (void)setExampleText:(NSString *)text image:(UIImage *)image;
+ (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;

代理命名

  • 名称以标示发送消息的对象的类名开头,省略类名的前缀并⼩小写第⼀个字⺟
- (BOOL)tableView:(NSTableView*)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender 
           openFile:(NSString *)filename;
  • 冒号紧跟在类名之后(随后的那个参数表⽰委派的对象)。该规则不适用于只有一个 sender 参数的⽅法
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;

代码组织

  • 在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:
  • 结构按照功能模块划分
  1. 懒加载
  2. 控制器生命周期。
  3. 私有方法
  4. 通知方法
  5. 代理方法。
  6. 公共方法。
  7. 触发事件。

#pragma mark - lazy

- (NSArray *)array {
    if(!_array) {
    
    }
    return _array;
}



#pragma mark - Lifecycle

- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

#pragma mark - Private

- (void)privateMethod {}

#pragma mark - NSCopying
- (void)didReceiveNotification {}

#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - Custom Accessors
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

#pragma mark - Public
- (void)publicMethod {}
#pragma mark - IBActions

- (IBAction)submitData:(id)sender {}

点表示法

  • 应“仅”用于获取和改变属性。括号表示法用于所有其它实例。
应该:
NSInteger arrayCount = self.array.count;
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

不应该:
NSInteger arrayCount = [self.array count];
[view setBackgroundColor:[UIColor orangeColor]];
[[UIApplication sharedApplication] delegate];

空格

  • 类方法声明在方法类型与返回类型之间要有空格。
- (void)methodName:(NSString *)string;

  • 条件判断的括号内侧不应有空格。
if (a < b) {
    // something
}

  • 关系运算符(如 >=、!=)和逻辑运算符(如 &&、||)两边要有空格。
(someValue > 100)? YES : NO

Literals字面量

  • 在创建NSString,NSDictionary,NSArrayNSNumber等对象的immutable实例时,应使用字面量。需要注意的是,不应将nil传递给NSArrayNSDictionary字面量,否则会引起程序崩溃。
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018; 

Case语句

  • 大括号在case语句中并不是必须的,除非编译器强制要求。当一个case语句包含多行代码时,大括号应该加上。
switch (condition) {
  case 1:
    // ...
    break;
  case 2: {
    // ...
    // Multi-line example using braces
    break;
  }
  case 3:
    // ...
    break;
  default: 
    // ...
    break;
}

  • 有很多次,当相同代码被多个cases使用时,一个fall-through应该被使用。一个fall-through就是在case最后移除break语句,这样就能够允许执行流程跳转到下一个case值。为了代码更加清晰,一个fall-through需要注释一下。
switch (condition) {
  case 1:
    // ** fall-through! **
  case 2:
    // code executed for values 1 and 2
    break;
  default: 
    // ...
    break;
}

布尔变量

  • 因为nil将被解析为NO,因此没有必要在条件语句中进行比较。永远不要将任何东西和YES进行直接比较,因为YES被定义为1,而一个BOOL变量可以有8个字节。
if (someObject) {}
if (![anotherObject boolValue]) {}
  • 如果BOOL属性的名字是一个形容词,属性就能忽略"is"前缀,但要指定get访问器的惯用名称。例如:
@property (assign, getter=isEditable) BOOL editable;

条件语句

  • 为避免错误,条件语句体必须使用大括号,即便语句体中的语句可以不必使用大括号(比如只有一行语句)。常见的错误包括在不使用大括号的情况下添加第二行语句,以为它属于if语句的一部分。此外,更可怕的事情是,如果条件语句中的代码行被注释,则本不属于条件语句的下一行代码将变成条件语句的一部分。此外,这种编码风格和所有其它条件语句均保持一致。
应该:
if (!error) {
  return success;
}
不应该:
if (!error)
  return success;
  
 if (!error) return success;

三元操作符

  • 仅当使用该运算子可以让代码显得更清晰易懂时方可使用三元运算子。更多情况下应使用条件语句。使用类似if的条件语句对多种条件进行判断通常要更容易理解,或使用实例变量。
应该:
result = a > b ? x : y; 
不应该:
result = a > b ? x = c > d ? c : d : y; 

注释

  • 当需要注释时,注释应该用来解释这段特殊代码为什么要这样做。任何被使用的注释都必须保持最新或被删除。

  • 一般都避免使用块注释,因为代码尽可能做到自解释,只有当断断续续或几行代码时才需要注释。例外:这不应用在生成文档的注释

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,060评论 0 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,303评论 18 399
  • 星期五,晴 早上老牛起床丸子已拉完,拉对了,就是尿在外面一点。 昨天晚上又提起要把小狗送人的事,老牛同意了,小羊跟...
    白罗汉阅读 213评论 0 0
  • 近两天有几个朋友问我如何生成代币,最简单的方法是看以太坊区块浏览器中的已经发行的代币是怎么写的,不明白的地方再去查...
    十三流阅读 6,696评论 2 3
  • 绛妖精 1.提升自己的核心价值,让男人离不开你。 1.观赏性价值。年轻貌美身材好。赏心悦目。严格控制体重,美容护肤...
    Cher素心若雪阅读 398评论 0 1