2019-06-20

先手围棋项目代码编写标准(iOS)

文件夹命名

采取英文命名(中文解释其含义),遵循驼峰原则,首字母大写的规范

例:

GoodCourse(好课)

屏幕快照 2019-06-24 18.50.40.png

文件夹下面遵循MVC原则建立子文件夹,首字母大写
GoodCourse(好课)
>Controller
>Model
>View

屏幕快照 2019-06-24 18.54.58.png

大模块下子文件按照功能划分文件夹,大模块下不建议放MVC,建议放子功能文件夹
Studio(工作室)
>BonusActivities(奖金活动)
>MedicalAssociation(我的医社)

屏幕快照 2019-06-25 17.50.58.png

1.类的命名

类名应该以三个大写字母作为前缀(双字母前缀为 Apple 的类预留)。尽管这个规范看起来有些古怪,但是这样做可以减少 Objective-C 没有命名空间所带来的问题。

一些开发者在定义模型对象时并不遵循这个规范(对于 Core Data 对象,我们更应该遵循这个规范)。我们建议在定义 Core Data 对象时严格遵循这个约定,因为最终你可能需要把你的 Managed Object Model(托管对象模型)与其他(第三方库)的 MOMs(Managed Object Model)合并。

你可能注意到了,这本书里类的前缀(不仅仅是类,也包括公开的常量、Protocol 等的前缀)是ZOC

另一个好的类的命名规范:当你创建一个子类的时候,你应该把说明性的部分放在前缀和父类名的在中间。

举个例子:如果你有一个 ZOCNetworkClient 类,子类的名字会是ZOCTwitterNetworkClient (注意"Twitter""ZOC""NetworkClient" 之间); 按照这个约定, 一个UIViewController 的子类会是 ZOCTimelineViewController.

2.通用的约定

尽可能遵守 Apple 的命名约定,尤其是和 内存管理规则 (NARC) 相关的地方。

推荐使用长的、描述性的方法和变量名。

推荐:

UIButton *settingsButton;

不推荐:

UIButton *setBut;

3.常量

常量应该以驼峰法命名,并以相关类名作为前缀

推荐:

static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;

不推荐:

static const NSTimeInterval fadeOutTime = 0.4;

推荐使用常量来代替字符串字面值数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用 static声明为静态常量,而不要用 #define,除非它明确的作为一个宏来使用。

推荐:

static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const CGFloat ZOCImageThumbnailHeight = 50.0f;

不推荐:

#define CompanyName @"Apple Inc."
#define magicNumber 42

4.属性

属性应该尽可能描述性地命名,避免缩写,并且是小写字母开头的驼峰命名。我们的工具可以很方便地帮我们自动补全所有东西(嗯。。几乎所有的,XcodeDerived Data 会索引这些命名)。所以没理由少打几个字符了,并且最好尽可能在你源码里表达更多东西。

推荐:

NSString *text;

不推荐:

NSString* text;
NSString * text;

5.点符号

当使用setter getter方法的时候尽量使用点符号。应该总是用点符号来访问以及设置属性。

推荐:

view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

不推荐:

[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

使用点符号会让表达更加清晰并且帮助区分属性访问和方法调用

6.属性定义

推荐按照下面的格式来定义属性

@property (nonatomic, readwrite, copy) NSString *name;

属性的参数应该按照下面的顺序排列:原子性读写内存管理。 这样做你的属性更容易修改正确,并且更好阅读。(译者注:习惯上修改某个属性的修饰符时,一般从属性名从右向左搜索需要修动的修饰符。最可能从最右边开始修改这些属性的修饰符,根据经验这些修饰符被修改的可能性从高到底应为:内存管理 > 读写权限 >原子操作)

你必须使用 nonatomic,除非特别需要的情况。在iOS中,atomic带来的锁特别影响性能。

属性可以存储一个代码块。为了让它存活到定义的块的结束,必须使用 copyblock 最早在栈里面创建,使用copyblock 拷贝到堆里面去)

为了完成一个共有的 getter和一个私有的 setter,你应该声明公开的属性为 readonly 并且在类扩展中重新定义通用的属性为 readwrite 的。

// .h文件中
@interface MyClass : NSObject
@property (nonatomic, readonly, strong) NSObject *object;
@end
// .m文件中
@interface MyClass ()
@property (nonatomic, readwrite, strong) NSObject *object;
@end

@implementation MyClass
// Do Something cool
@end

描述BOOL属性的词如果是形容词,那么setter不应该带is前缀,但它对应的 getter 访问器应该带上这个前缀,如:

@property (assign, getter=isEditable) BOOL editable;

在实现文件中应避免使用@synthesize,因为Xcode已经自动为你添加了

7.私有属性

私有属性应该定义在类的实现文件的类的扩展 (匿名的 category) 中。不允许在有名字的 category(如 ZOCPrivate)中定义私有属性,除非你扩展其他类。

例子:

@interface ZOCViewController ()
@property (nonatomic, strong) UIView *bannerView;
@end

8.可变对象

任何可以用一个可变的对象设置的((比如 NSString,NSArray,NSURLReques))属性的内存管理类型必须是 copy

9.私有方法

永远不要在你的私有方法前加上_ 前缀。这个前缀是 Apple 保留的。不要冒重写苹果的私有方法的险。

10.Categories

虽然我们知道这样写很丑, 但是我们应该要在我们的 category 方法前加上自己的小写前缀以及下划线,比如- (id)zoc_myCategoryMethod。 这种实践同样被苹果推荐。

这是非常必要的。因为如果在扩展的 category 或者其他 category 里面已经使用了同样的方法名,会导致不可预计的后果。实际上,实际被调用的是最后被加载的那个 category中方法的实现(译者注:如果导入的多个 category 中有一些同名的方法导入到类里时,最终调用哪个是由编译时的加载顺序来决定的,最后一个加载进来的方法会覆盖之前的方法)。

如果想要确认你的分类方法没有覆盖其他实现的话,可以把环境变量OBJC_PRINT_REPLACED_METHODS 设置为 YES,这样那些被取代的方法名字会打印到 Console 中。现在 LLVM 5.1 不会为此发出任何警告和错误提示,所以自己小心不要在分类中重写方法。

一个好的实践是在 category 名中使用前缀。

推荐:

@interface NSDate (ZOCTimeExtensions)
- (NSString *)zoc_timeAgoShort;
@end

不推荐:

@interface NSDate (ZOCTimeExtensions)
- (NSString *)timeAgoShort;
@end

11.NSNotification

当你定义你自己的 NSNotification 的时候你应该把你的通知的名字定义为一个字符串常量,就像你暴露给其他类的其他字符串常量一样。你应该在公开的接口文件中将其声明为 extern 的, 并且在对应的实现文件里面定义。

因为你在头文件中暴露了符号,所以你应该按照统一的命名空间前缀法则,用类名前缀作为这个通知名字的前缀

同时,用一个 Did/Will 这样的动词以及用 "Notifications" 后缀来命名这个通知也是一个好的实践

// Foo.h
extern NSString * const ZOCFooDidBecomeBarNotification

// Foo.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";

12.美化代码

空格

缩进使用4 个空格。 永远不要使用 tab, 确保你在 Xcode的设置里面是这样设置的。
方法的大括号和其他的大括号(if/else/switch/while 等) 总是在同一行开始,在新起一行结束。

推荐:

if (user.isHappy) {
    // Do something
}
else {
    // Do something else
}

不推荐:

if (user.isHappy)
{
  // Do something
} else {
  // Do something else
}
  • 方法之间应该要有一个空行来帮助代码看起来清晰且有组织。 方法内的空格应该用来分离功能,但是通常不同的功能应该用新的方法来定义。
  • 优先使用 auto-synthesis。但是如果必要的话, @synthesize and @dynamic
  • 在实现文件中的声明应该新起一行。
  • 应该总是让冒号对齐。有一些方法签名可能超过三个冒号,用冒号对齐可以让代码更具有可读性。即使有代码块存在,也应该用冒号对齐方法。
[UIView animateWithDuration:1.0
                 animations:^{
                     // something
                 }
                 completion:^(BOOL finished) {
                     // something
                 }];

不推荐:

[UIView animateWithDuration:1.0 animations:^{
    // something
} completion:^(BOOL finished) {
    // something
}];

13.代码组织

来自 Mattt Thompson

code organization is a matter of hygiene (代码组织是卫生问题)

我们十分赞成这句话。清晰地组织代码和规范地进行定义, 是你对自己以及其他阅读代码的人的尊重。

利用代码块

一个 GCC 非常模糊的特性,以及 Clang 也有的特性是,代码块如果在闭合的圆括号内的话,会返回最后语句的值

NSURL *url = ({
    NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
    [NSURL URLWithString:urlString];
});

Pragma

Pragma Mark
#pragma mark -是一个在类内部组织代码并且帮助你分组方法实现的好办法。 我们建议使用 #pragma mark - 来分离:

  • 不同功能组的方法
  • protocols 的实现
  • 对父类方法的重写
- (void)dealloc { /* ... */ }
- (instancetype)init { /* ... */ }

#pragma mark - View Lifecycle (View 的生命周期)

- (void)viewDidLoad { /* ... */ }
- (void)viewWillAppear:(BOOL)animated { /* ... */ }
- (void)didReceiveMemoryWarning { /* ... */ }

#pragma mark - Custom Accessors (自定义访问器)

- (void)setCustomProperty:(id)value { /* ... */ }
- (id)customProperty { /* ... */ }

#pragma mark - IBActions  

- (IBAction)submitData:(id)sender { /* ... */ }

#pragma mark - Public

- (void)publicMethod { /* ... */ }

#pragma mark - Private

- (void)zoc_privateMethod { /* ... */ }

#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

#pragma mark - ZOCSuperclass

// ... 重写来自 ZOCSuperclass 的方法

#pragma mark - NSObject

- (NSString *)description { /* ... */ }

上面的标记能明显分离和组织代码。你还可以用 cmd+Click 来快速跳转到符号定义地方。 但是小心,即使paragma mark 是一门手艺,但是它不是让你类里面方法数量增加的一个理由:类里面有太多方法说明类做了太多事情,需要考虑重构了。

关于 pragma

在 http://raptureinvenice.com/pragmas-arent-just-for-marks/ 有很好的关于 pragma的讨论了,在这边我们再做部分说明。

大多数 iOS 开发者平时并没有和很多编译器选项打交道。一些选项是对控制严格检查(或者不检查)你的代码或者错误的。有时候,你想要用 pragma 直接产生一个异常,临时打断编译器的行为。

当你使用ARC的时候,编译器帮你插入了内存管理相关的调用。但是这样可能产生一些烦人的事情。比如你使用NSSelectorFromString 来动态地产生一个 selector 调用的时候,ARC不知道这个方法是哪个并且不知道应该用那种内存管理方法,你会被提示 performSelector may cause a leak because its selector is unknown(执行 selector 可能导致泄漏,因为这个 selector 是未知的).

如果你知道你的代码不会导致内存泄露,你可以通过加入这些代码忽略这些警告

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

[myObj performSelector:mySelector withObject:name];

#pragma clang diagnostic pop

注意我们是如何在相关代码上下文中用 pragma 停用-Warc-performSelector-leaks 检查的。这确保我们没有全局禁用。如果全局禁用,可能会导致错误。

全部的选项可以在 The Clang User's Manual 找到并且学习。

你可能感兴趣的:(2019-06-20)