本文書は、CookpadにおけるObjective-C コードのスタイル規準を定めるものである。
Appleのスタイルを基本として採用する。理由はAppleのフレームワークを多用するため、Apple スタイルのヘッダファイルを見ることが多いためである。
Cookpadにおける標準的なスタイルを定めている。また、Swiftへの移行を見越して、複数の選択肢があるときはSwiftに近いスタイルを採用する。なお、「正しいコードを書く」というのは前提なので、そのためのtipsなどは記載しない。
- [MUST] 公開する必要のないプロパティやメソッドの定義はクラス拡張を使用して実装ファイルに記述する。ヘッダファイルには公開メソッドとプロパティのみを記述すること
// Bad
// ViewController.h
@interface ViewController : UIViewController
@property (nonatomic, weak) IBOutlet UILabel *label;
@property (nonatomic, weak) IBOutlet UIButton *button;
@property (nonatomic, weak) IBOutlet UITextView *textView;
@end
// Good
// ViewController.m
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UILabel *label;
@property (nonatomic, weak) IBOutlet UIButton *button;
@property (nonatomic, weak) IBOutlet UITextView *textView;
@end
@implementation ViewController
@end
- [MUST] プロトコルの適用と実装も公開の必要がなければ実装ファイルでクラス拡張により行う
- もちろん公開の必要があればヘッダファイルに書いてよい
// Bad
// ViewController.h
@interface ViewController : UIViewController <UITableViewDataSource>
@end
// Good
// ViewController.m
@interface ViewController () <UITableViewDataSource>
@end
@implementation ViewController
@end
- [MUST] プライベートメソッドには、
_
プレフィックスを付けてはいけない
// Bad
- (void)_privateMethod1
{
// ...
}
// Good
- (void)privateMethod1
{
// ...
}
- [MUST] オーバーライドした時に、確実に親クラスのメソッドを呼んでほしいメソッドには
NS_REQUIRES_SUPER
をつけること
- [MUST] プロパティの属性として
nonatomic
をつけること- atomicによってスレッドセーフになるのは実質的に構造体のみと考えられるため
- またatomicであってもそのクラス自体がスレッドセーフになるわけではない
- [MUST]
atomic
をつける妥当な理由がある場合は、それをコメントに書いたうえで使うこと - [MUST] プロパティの属性として
weak
を使える場所では使うこと
// Bad
@property (nonatomic) id delegate;
@property (nonatomic) IBOutlet UILabel *label;
// Good
@property (nonatomic, weak) id<FooDelegate> delegate;
@property (nonatomic, weak) IBOutlet UILabel *label;
- [MUST] 代入が必要ないプロパティはreadonlyにすること
- できるだけ副作用の少ないプログラムにするため
// Bad
@interface OriginalView
@property (nonatomic, weak) UILabel *label; // 例えば、このlabelのtextは変更したいがlabel自体は変更しないケース
@end
// Good
@interface OriginalView
@property (nonatomic, readonly) UILabel *label;
@end
- [MUST]
assign
,strong
は冗長なので指定しないこと - [MUST]
NSString
,NSArray
,NSDictionary
,NSSet
のプロパティはcopy
を指定すること- 実体がimmutableなオブジェクトであればメモリ割り当てのコストは発生しないし、mutableなオブジェクトであれば安全のためにcopyすべきであるため
- その他、必要に応じて
NSCopying
プロトコルに準拠するクラスにはcopy
を指定すること
- [MUST] 原則としてインスタンス変数は宣言せず、必要なプロパティは
@property
として宣言すること- インスタンス変数名でアクセスするのはゲッター、セッター、イニシャライズメソッドでのみにすること
// Bad
@interface ViewController () <UITableViewDataSource>
{
NSString *_recipeID;
}
@end
// Good
@interface ViewController () <UITableViewDataSource>
@property (nonatomic, copy) NSString *recipeID;
@end
- [MUST] プロパティへのアクセスはメソッド呼び出しではなくドット記法を使うこと
// Bad
[view setBackgroundColor:[UIColor whiteColor]];
// Good
view.backgroundColor = [UIColor whiteColor];
- [MUST] プロパティの宣言などでカラムの位置は調整しなくてよい
- 修正時の差分を最小にするため
// Bad
@property (nonatomic) NSInteger foo;
@property (nonatomic, copy) NSString *bar;
// Good
@property (nonatomic) NSInteger foo;
@property (nonatomic, copy) NSString *bar;
- [MUST] スコープがもっとも狭くなるように宣言すること
- [MUST] 変数の再利用をしてはいけない
- 同じ型の変数であっても、用途が違う場合は都度用途に適した名前を付けて宣言すること
- [MUST] 定数としては
extern NSString *const FooBar
またはNS_ENUM
を使うこと- 定数としてマクロを使ってはならない
k
Prefix は推奨しない。定数名は大文字始まりにすること- ソースコードファイルをまたいで使用したい定数を宣言する場合は
extern
指定子を用い、クラス名を頭につけた名前にすること - ファイルスコープの定数を宣言する場合は
static
指定子を用いること。クラス名やベンダープレフィックスを名前に含めない
- [MUST] ポインタのアスタリスクは型ではなく変数につけること
- [MUST] 原則として生の
id
型を使用してはいけない- CocoaTouchの仕様上必要な場合を除き、明示的に使う必要はないはずである
- プロトコルつきの
id<Protocol>
はこの限りではない - 基底クラスが同一の様々なクラスとなりうる型を表現する際は
__kindof
キーワードをつけること
- [MUST] コレクション型にはジェネリクスを使い要素の型を指定すること
- 例えば
NSArray<NSString *> *
などのように宣言する - ジェネリクスにできない場合は設計が間違っている可能性がある
- 例えば
- [SHOULD]
nullable
/nonnull
が使える場所ではなるべく使うこと - [SHOULD] 可能な場合は
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
マクロを使い、nonnull
は明示しないこと
// Bad
@interface SomeClass
@property (nonatomic, nullable, copy) NSString *nullableString;
@property (nonatomic, nonnull, copy) NSString *nonnullString;
@end
// Good
NS_ASSUME_NONNULL_BEGIN
@interface SomeClass
@property (nonatomic, nullable, copy) NSString *nullableString;
@property (nonatomic, copy) NSString *nonnullString;
@end
NS_ASSUME_NONNULL_END
- [MUST] enumでなく
NS_ENUM
を使う
// Bad
typedef enum {
TypeNone = 0,
TypeDone = 1,
} Type;
// Good
typedef NS_ENUM(char, Type) {
TypeNone = 0,
TypeDone = 1,
};
後者だとType
型として定義できる数値を限定できる。例えば、誤ってTypeNG = 0xFF
とかを加えてしまったときにコンパイルエラーを出してくれる。
- [MUST] ビットフラグを定義する場合は、
NS_OPTIONS
を使う
- [MUST] UIViewControllerを親に持つControllerはYYYAbcViewController、その他のControllerはYYYAbcControllerとする(YYYAbcを任意の名称で)。
- [MUST] その他UIKitフレームワークのUIXXXを継承する場合は、基本的にYYYAbcXXXとする。ただしUITableViewCellなど長いクラス名で末尾の単語のみ残せば意味が通るものは例外とする。
// Samples
UIView *originalView = [CKDOriginalView new];
UIButton *originalButton = [CKDOriginalButton buttonWithType:UIButtonTypeCustom];
UITextField *originalTextField = [CKDOriginalTextField new];
// 例外
UITableViewCell *originalCell = [[CKDOriginalCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"XXX"];
- [MUST] UIKit や各種ライブラリなどのプロジェクト外で定義されているクラスに対してカテゴリでメソッドを追加する場合は、メソッド名にベンダープレフィックスをつけること
// Bad
@interface UIImageView (CKDRecipeThumbnailUtil)
- (void)setRecipeThumbnailImageWithURL:(NSURL *)url;
@end
// Good
@interface UIImageView (CKDRecipeThumbnailUtil)
- (void)ckd_setRecipeThumbnailImageWithURL:(NSURL *)url;
@end
- [MUST] Objective-C Literalsを使うこと
// Bad
NSNumber *answer = [NSNumber numberWithInt:42];
NSArray *array = [NSArray arrayWithObjects:@"one", @"two", nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"value", @"key", nil];
// Good
NSNumber *answer = @42;
NSArray *array = @[@"one", @"two"];
NSDictionary *dictionary = @{@"key" : @"value"};
-
[SHOULD] インクリメント、デクリメントは for の再初期化式以外では使わないこと
- Swift では廃止されるため、使用を避けること
- CoreFoundation API などを使った低レイヤの処理を書く際に必要な場合はこの限りではない
-
[MUST]
nil
の条件判定はif (x == nil)
またはif (x != nil)
とする
// Bad
if (nil != x) { /* ... */ }
if (nil == x) { /* ... */ }
if (!x) { /* ... */ }
if (x) { /* ... */ }
// Good
if (x != nil) { /* ... */ }
if (x == nil) { /* ... */ }
- [MUST]
__block
変数は原則として使わないこと- 必要なケースはあるが、ほとんどの場合使わずに済むように書き直せる
- たとえば、
- enumerateObjectsUsingBlock:
は for-each文に書き換えられる