iOS编码建议

iOS编码建议

Posted by Ted on January 2, 2019

一、ViewController代码结构

在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,遵循以下结构:


// 生命周期
#pragma mark - Lifecycle

- (instancetype)init {}

- (void)awakeFromNib {}

- (void)loadView {}

- (void)viewDidLoad {}

- (void)viewWillAppear:(BOOL)animated {}

- (void)viewWillLayoutSubviews {}

- (void)viewDidLayoutSubviews {}

- (void)viewDidAppear:(BOOL)animated {}

- (void)viewWillDisappear:(BOOL)animated {}

- (void)viewDidDisappear:(BOOL)animated {}

- (void)dealloc {}

- (void)didReceiveMemoryWarning {}


// 如果是Storyboard/Xib
#pragma mark - IBActions

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

// 公开方法
#pragma mark - Public
- (void)publicMethod {}

// 私有方法
#pragma mark - Private
- (void)privateMethod {}

// 各种代理和协议方法
#pragma mark - Protocol
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

// 懒加载的Getter
#pragma mark - Getter && Setter

// Copy协议和description较少
#pragma mark - NSCopying/NSObject
- (id)copyWithZone:(NSZone *)zone {}
- (NSString *)description {}

二、命名

1、变量和方法名:驼峰命名;

2、宏和常量命名

  • #define 预处理定义的常量全部大写,单词间用 _ 分隔

    例子: #define THIS_IS_AN_MACRO @”THIS_IS_AN_MACRO”

  • 宏定义的本质是在编译时进行替换,所以宏定义中如果包含表达式或变量,表达式或变量必须用小括号括起来,防止与其他变量出现混合的情况。

3、枚举:枚举类型参考系统应该写全类型:名称+类型

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
};

当在switch使用枚举类型时,’default’是不需要的

4、类的命名: 

ViewController: 使用ViewController做后缀,例子: XXHomeViewController

View: 使用View做后缀,例子: XXAlertView

UITableCell:使用Cell做后缀,例子: XXNewsCell

Protocol: 使用Delegate或者DataSource作为后缀,例子: UITableViewDelegate

UI控件依次类推

三、编码

1、字面编码

NSString, NSDictionary, NSArray, 和 NSNumber可以在创建不可变值时使用,注意不要传nil值,否则会崩溃。

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

2、条件语句

条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。

// 应该
if (!error) {
  return success;
}

// 而不是
if (!error)
  return success;
// 或者
if (!error) return success;

3、构造方法/init方法

当构造方法被使用时,虽然用id也可以通过编译,但它应该返回类型是instancetype而不是id。这样确保编译器正确地推断结果类型。

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

四、变量和属性

建议使用属性而不是实例变量。

推荐:

@interface RWTTutorial : NSObject
@property (strong, nonatomic) NSString *tutorialName;
@end
    
self.tutorialName

不推荐:

@interface RWTTutorial : NSObject {
  NSString *tutorialName;
}

self->tutorialName
  • 属性会自动有Setter和Getter,可以通过直接用点语法访问Setter和Getter方法。
  • 虽然*. 和 ->是等价的,或者说编译器优化后等价。但是在访问空指针和野指针时有差别——空指针会挂在*解引用,野指针会挂在.访问内存。

五、常量

共享一块内存空间,就算项目中N处用到,也不会分配N块内存空间,可以根据const修饰的位置设定能否修改,在编译阶段会执行类型检查

推荐使用const来定义常量,如下

NSString *const kWEGMomentTopicNumberUpdateNotification = @"kWEGMomentTopicNumberUpdateNotification";

const不能满足的情况再考虑使用宏定义

#define kWEGMomentTopicNumberUpdateNotification @"kWEGMomentTopicNumberUpdateNotification"

如果是需要外部共享,使用extern关键字,参考苹果官方

extern NSString * const UIApplicationDidEnterBackgroundNotification  
extern NSString * const UIApplicationWillEnterForegroundNotification  
extern NSString * const UIApplicationDidFinishLaunchingNotification;

六、整体架构

推荐MVC+VM(也叫MVCS,S是Store的意思,数据处理逻辑)。

MVC+VM:MVC是苹果官方推荐,V专注于视图构建,Model是瘦Model,专注于模型构建,仅持有数据;数据流动全部由Controller来传递,流向清晰,当Controller变复杂时,将数据处理逻辑剥离出来成为VM,模型数据流向仍由Controller负责,优点是数据流向清晰,业务耦合弱,缺点是不同Controller可能要生成各种不同VM适配。

MVVM:主要优点数据绑定,做到数据一致性,同时也能做到给Controller瘦身目的,缺点是数据绑定导致数据流向不清晰,难以调试Bug,如果没有详细清晰文档,很可能成为一个天坑(助手里的聊天模块)。

MVP:Presenter与Controller相互持有,通过接口,P层拥有了Controller的权利,所有的业务分配都在P层内完成,包括像tableView的数据源和代理。优点是MVC解耦,MVC成为平行;缺点是,有点特意为了解耦而解耦,初看会莫名其妙。

一份分享

Android代码整洁