Objective-Cコーディング標準:iOS開発の問題を解決するための26の側面

前書き

Objective-Cコーディング標準を開発した理由、書籍、チュートリアル、スターターキットでコードを洗練された一貫性のある状態に保つことができるためです。私たちが異なる本を完成させるために多くの異なる著者がいる場合でも。

ここでのコーディング標準は、主に印刷とWebの読みやすさを目的としているため、これまでに見た他のObjective-Cコーディング標準とは異なる場合があります。

著者について

このコーディング標準の作成は、Nicholas Waynikのリーダーシップの下、raywenderlich.comの多くのチームメンバーによって完了しました。チームメンバー:Soheil Moayedi Azarpour、Ricardo Rendon Cepeda、Tony Dahbura、Colin Eberhardt、Matt Galloway、Greg Heo、Matthijs Hollemans、Christopher LaPollo、Saul Mora、Andy Pereira、Mic Pringle、Pie Todo Rea、Marisare Rocそしてレイ・ウェンダリッヒ。

New York TimesとRobots&PencilsのObjective-Cコーディング標準の作成者にも感謝しています。これら2つのコーディング標準は、このガイドを作成するための良い出発点となります。

バックグラウンド

コーディングスタイルに関するAppleの公式ドキュメントがいくつかありますが、特に言及がない場合は、次のドキュメントで詳細を確認できます。

言語

米国英語を使用する必要があります。

する必要があります:

UIColor *myColor = [UIColor whiteColor];  

いけない:

UIColor *myColour = [UIColor whiteColor];  

コード編成

#pragma mark-toを使用して、一般的な構造に従って、関数のグループ化とプロトコル/デリゲートの実装でメソッドを分類します。

#pragma mark - Lifecycle  
- (instancetype)init {}  
- (void)dealloc {}  
- (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)privateMethod {}  
#pragma mark - Protocol conformance  
#pragma mark - UITextFieldDelegate  
#pragma mark - UITableViewDataSource  
#pragma mark - UITableViewDelegate  
#pragma mark - NSCopying  
- (id)copyWithZone:(NSZone *)zone {}  
#pragma mark - NSObject  
- (NSString *)description {}  

スペース

  • インデントには4つのスペースを使用します。Xcodeの設定で必ず設定してください。(Raywenderlich.comは2つのスペースを使用します)
  • メソッド中括弧と他の中括弧(if / else / switch / whileなど)は常に同じ行で開きますが、新しい行で閉じます。

する必要があります:

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

いけない:

if (user.isHappy)  
{  
  //Do something  
}  
else {  
  //Do something else  
}  
  • メソッド間には1行のみが必要です。これにより、視覚的に明確で整理しやすくなります。メソッドのギャップは関数を分離する必要がありますが、通常はすべて引き出されて新しいメソッドになります。
  • 最初に自動合成を使用します。ただし、必要に応じて、@ synthesizeと@dynamicはそれぞれ実装で新しい行を宣言する必要があります。
  • コロンで整列した方法でメソッドを呼び出すことは避けてください。メソッドシグネチャが3つ以上のコロンを含むことがあり、コロンの配置によりコードが読みやすくなる場合があります。Xcodeのアラインメントはコードを読みにくくするため、コロンアラインメントメソッドにはコードブロックが含まれていますが、これは行わないでください。

する必要があります:

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

いけない:

// colon-aligning makes the block indentation hard to read  
[UIView animateWithDuration:1.0  
                 animations:^{  
                     // something  
                 }  
                 completion:^(BOOL finished) {  
                     // something  
                 }];  

コメント

コメントが必要な場合は、この特定のコードがそれを行う理由を説明するために使用する必要があります。使用したコメントは最新の状態に保つか、削除する必要があります。

コードは可能な限り説明が必要ないため、一般的にブロックコメントは避けられます。コメントは、コードが断続的または数行の場合にのみ必要です。例外:これは、生成されたドキュメントのコメントには適用されません

iOS開発iOSDevTipに注意を払い、「情報」と返信して2000Gプログラミング資料をダウンロードします。

名前

Appleの命名規則、特にこれらに関連するメモリ管理規則(NARC)は可能な限り一貫しています。

長く説明的な方法と変数の名前付けが適しています。

する必要があります:

UIButton *settingsButton;  

いけない:

UIButton *setBut;  

3文字の接頭辞は、多くの場合、クラスと定数の命名で使用する必要がありますが、コアデータエンティティ名では無視する必要があります。raywenderlich.comの公式書籍、スターターキット、チュートリアルの場合は、接頭辞「RWT」を使用する必要があります。

定数は、キャメルケースの命名規則を使用する必要があります。すべての単語には、クラス名に関連する最初の大文字と接頭辞が付きます。

する必要があります:

static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3;  

いけない:

static NSTimeInterval const fadetime = 1.7;  

属性もキャメルケースを使用しますが、最初の単語の最初の文字は小文字です。正当な理由がない限り、@ synthesizeステートメントを手動で記述する代わりに、プロパティに自動合成を使用します。

する必要があります:

@property (strong, nonatomic) NSString *descriptiveVariableName;  

いけない:

id varnm;  

下線

属性を使用する場合、インスタンス変数は、selfを使用してアクセスおよび変更する必要があります。これは、属性の前に自己があるため、すべての属性が異なる視覚効果を持つことを意味します。

ただし、特殊なケースがあります。初期化メソッドでは、インスタンス変数(_variableNameなど)を直接使用して、ゲッター/セッターの潜在的な副作用を回避する必要があります。

ローカル変数にはアンダースコアを含めないでください。

方法

メソッドシグネチャでは、メソッドタイプの後にスペースが必要です(-/ +記号)。メソッドの各セクションの間にもスペースが必要です(Appleのスタイルに従って)。パラメータを説明するために、パラメータの前に説明的なキーワードを含める必要があります。

「and」という単語の使用は保持する必要があります。次のinitWithWidth:heightの例のように、複数のパラメーターを使用して説明することはできません。

する必要があります:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;  
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;  
- (id)viewWithTag:(NSInteger)tag;  
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;  

いけない:

-(void)setT:(NSString *)text i:(UIImage *)image;  
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;  
- (id)taggedView:(NSInteger)tag;  
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;  
- (instancetype)initWith:(int)width and:(int)height;  // Never do this.  

変数

変数にはわかりやすい名前を付けてください。1文字の変数の命名は、for()ループを除いて、可能な限り回避する必要があります。

アスタリスクは、変数がポインターであることを示します。例:
NSString * text 

どちらのNSString *テキストも 

いくつかの特別な場合の定数を除いて、それはNSString *テキストでもありません。

プライベート変数は、インスタンス変数の使用を可能な限り置き換える必要があります。インスタンス変数の使用は効果的な方法ですが、コードの一貫性を維持するために属性を使用する傾向があります。

「back」属性(変数名の前にアンダースコアを付けた_variable)を使用してインスタンス変数に直接アクセスすることは、初期化メソッド(init、initWithCoder:など)、deallocメソッド、およびカスタムセッターとゲッターを除いて、できるだけ避けてください。初期化メソッドで直接Accessorメソッドを使用してdeallocする方法の詳細については、ここを確認してください。

する必要があります:

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

いけない:

@interface RWTTutorial : NSObject {  
  NSString *tutorialName;  
}  

属性の特性

初心者がコードを読みやすくするために、すべての属性特性を明示的にリストする必要があります。属性特性の順序は、ストレージ、原子性である必要があります。これは、Interface BuilderがUI要素に接続するときの自動コード生成と一致しています。

する必要があります:

@property (weak, nonatomic) IBOutlet UIView *containerView;  
@property (strong, nonatomic) NSString *tutorialName;  

いけない:

@property (nonatomic, weak) IBOutlet UIView *containerView;  
@property (nonatomic) NSString *tutorialName;  

NSStringは、強力な属性特性ではなくコピーを使用する必要があります。

どうして?NSStringプロパティを宣言したとしても、誰かがNSMutableStringのインスタンスを渡して、あなたの注意を払わずにそれを変更する可能性があります。

する必要があります:

@property (copy, nonatomic) NSString *tutorialName;  

いけない:

@property (strong, nonatomic) NSString *tutorialName;  

ドット表記構文

ポイント構文は、アクセスメソッド呼び出しをカプセル化する便利な方法です。ドット構文を使用する場合、プロパティは引き続きゲッターまたはセッターメソッドを使用してアクセスまたは変更されます。詳細については、https:  //developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.htmlをご覧ください

コードをより簡潔にするため、プロパティへのアクセスと変更に常にドット構文を使用する必要あります[]記号は、他の例で使用するために、より偏っています。

する必要があります:

objc  
NSInteger arrayCount = [self.array count];  
view.backgroundColor = [UIColor orangeColor];  
[UIApplication sharedApplication].delegate;  

いけない:

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

リテラル値

これらのクラスの不変のインスタンスを作成するときは、NSString、NSDictionary、NSArray、およびNSNumberのリテラル値を使用する必要があります。クラッシュの原因となるため、nil値をNSArrayおよびNSDictionaryリテラル値に渡すことができないことに特に注意してください。

する必要があります:

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

いけない:

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

絶え間ない

定数は繰り返し使用するのが簡単で、検索や置換を行わなくてもすばやく変更できます。明示的にマクロを使用しない限り、定数は#defineではなくstaticを使用して宣言する必要があります。

する必要があります:

static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";  
static CGFloat const RWTImageThumbnailHeight = 50.0;  

いけない:

#define CompanyName @"RayWenderlich.com"  
#define thumbnailHeight 2  

列挙型

enumを使用する場合は、型チェックとコード補完が強化されているため、新しい固定基本型仕様を使用することをお勧めします。これで、SDKにマクロNS_ENUM()が追加され、固定された基本タイプの使用を支援および奨励します。

例えば:

typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {  
  RWTLeftMenuTopItemMain,  
  RWTLeftMenuTopItemShows,  
  RWTLeftMenuTopItemSchedule  
};  

値を明示的に割り当てることもできます(古いkスタイルの定数定義を表示):

typedef NS_ENUM(NSInteger, RWTGlobalConstants) {  
  RWTPinSizeMin = 1,  
  RWTPinSizeMax = 5,  
  RWTPinCountMin = 100,  
  RWTPinCountMax = 500,  
};  

Core Foundation Cコードを記述しない限り、古いkスタイルの定数定義は避けてください。

いけない:

enum GlobalConstants {  
  kMaxPinSize = 5,  
  kMaxPinCount = 500,  
};  

ケースステートメント

コンパイラーで要求されない限り、caseステートメントでは中括弧は必要ありません。caseステートメントに複数行のコードが含まれている場合は、中括弧を追加する必要があります。

switch (condition) {  
  case 1:  
    // ...  
    break;  
  case 2: {  
    // ...  
    // Multi-line example using braces  
    break;  
  }  
  case 3:  
    // ...  
    break;  
  default:   
    // ...  
    break;  
}  

多くの場合、同じコードが複数のケースで使用される場合、フォールスルーを使用する必要があります。フォールスルーとは、ケースの最後にある「break」ステートメントを削除して、実行フローが次のケース値にジャンプできるようにすることです。コードをより明確にするために、フォールスルーにコメントする必要があります。

switch (condition) {  
  case 1:  
    // ** fall-through! **  
  case 2:  
    // code executed for values 1 and 2  
    break;  
  default:   
    // ...  
    break;  
}  

スイッチで列挙型を使用する場合、「デフォルト」は必要ありません。例えば:

RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;  
switch (menuType) {  
  case RWTLeftMenuTopItemMain:  
    // ...  
    break;  
  case RWTLeftMenuTopItemShows:  
    // ...  
    break;  
  case RWTLeftMenuTopItemSchedule:  
    // ...  
    break;  
}  

私有財産

プライベート属性は、クラスの実装ファイルのクラス拡張(匿名分類)で宣言する必要があり、名前付きクラス(RWTPrivateやプライベートなど)は、他のクラスを拡張しない限り使用しないでください。+ Private.hファイルの命名規則を使用して、匿名の分類をテストに公開する必要があります。

例えば:

@interface RWTDetailViewController ()  
@property (strong, nonatomic) GADBannerView *googleAdView;  
@property (strong, nonatomic) ADBannerView *iAdView;  
@property (strong, nonatomic) UIWebView *adXWebView;  
@end  

ブール値

Objective-CはYESとNOを使用します。trueとfalseはCoreFoundation、CまたはC ++コードでのみ使用する必要があるためです。nilはNOに解決されるため、条件ステートメントを比較する必要はありません。YESは1と定義されており、BOOLは8ビットに設定できるため、直接YESと比較しないでください。

これは、さまざまなファイルの一貫性を維持し、より視覚的に簡潔にするためです。

する必要があります:

if (someObject) {}  
if (![anotherObject boolValue]) {}  

いけない:

if (someObject == nil) {}  
if ([anotherObject boolValue] == NO) {}  
if (isAwesome == YES) {} // Never do this.  
if (isAwesome == true) {} // Never do this.  

BOOLプロパティの名前が形容詞の場合、プロパティは "is"プレフィックスを無視できますが、getアクセサーの慣用的な名前を指定できます。例えば:

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

本文と例はここからココア命名ガイドラインを参照しています。

条件付きステートメント

条件付きステートメントの本文を中括弧なしで記述できる場合でも(たとえば、1行のコードのみ)、エラーを防ぐために、条件付きステートメントの本文は中括弧で囲む必要があります。これらのエラーには、2行目のコードの追加と、それがifステートメントになることが予想されることが含まれます。さらに、ifステートメントの1行のコードがコメント化され、次のコード行が無意識のうちにifステートメントの一部になると、さらに危険な欠陥が発生する可能性があります。さらに、このスタイルは他の条件ステートメントと一致しているため、読みやすくなっています。

する必要があります:

if (!error) {  
  return success;  
}  

いけない:

if (!error)  
  return success;  

または

if (!error) return success;  

三項演算子

コードの明快さと簡潔さを改善する必要がある場合、三項演算子?:が使用されます。多くの場合、単一条件の評価に必要です。複数の条件が評価される場合、ifステートメントを使用するかインスタンス変数を再構成すると、コードが読みやすくなります。一般的に言えば、条件に基づいて値を割り当てるときは、三項演算子を使用するのが最善です。

非ブール変数は何かと比較され、括弧()は読みやすさを向上させます。比較される変数がブール型である場合、括弧は必要ありません。

する必要があります:

NSInteger value = 5;  
result = (value != 0) ? x : y;  
BOOL isHorizontal = YES;  
result = isHorizontal ? x : y;  

いけない:

result = a > b ? x = c > d ? c : d : y;  

初期化メソッド

Initメソッドはコードテンプレートを生成するためのAppleの命名規則に従う必要があり、戻り値の型はidではなくinstancetypeを使用する必要があります。

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

クラスコンストラクタ

クラスコンストラクターを使用すると、idではなくinstancetypeが返されます。これにより、コンパイラが結果の型を正しく推論できるようになります。

@interface Airplane  
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;  
@end  

インスタンスタイプの詳細については、NSHipster.comを確認してください。

CGRect関数

CGRectでx、y、幅、または高さにアクセスする場合、構造体から直接アクセスする代わりに、CGGeometry関数を使用する必要があります。AppleのCGGeometryを引用すると:

  • このリファレンスドキュメントのすべての関数は、CGRect構造を入力として受け入れ、結果を計算するときにこれらの四角形を暗黙的に正規化します。したがって、アプリケーションでは、CGRectデータ構造に格納されているデータに直接アクセスして変更することは避けてください。代わりに、これらの関数を使用して長方形を操作し、それらの特性を取得します。

する必要があります:

CGRect frame = self.view.frame;  
CGFloat x = CGRectGetMinX(frame);  
CGFloat y = CGRectGetMinY(frame);  
CGFloat width = CGRectGetWidth(frame);  
CGFloat height = CGRectGetHeight(frame);  
CGRect frame = CGRectMake(0.0, 0.0, width, height);  

いけない:

CGRect frame = self.view.frame;  
CGFloat x = frame.origin.x;  
CGFloat y = frame.origin.y;  
CGFloat width = frame.size.width;  
CGFloat height = frame.size.height;  
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

黄金の道

条件付きステートメントでコーディングする場合、左側のコードは「ゴールデン」または「ハッピー」パスである必要があります。つまり、ifステートメントをネストしないでください。複数のreturnステートメントもOKです。

する必要があります:

- (void)someMethod {  
  if (![someOther boolValue]) {  
    return;  
  }  
  //Do something important  
}  

いけない:

- (void)someMethod {  
  if ([someOther boolValue]) {  
    //Do something important  
  }  
}  

エラー処理

メソッドが参照によってエラーパラメータを返す場合、エラー変数ではなく戻り値を判断します。

する必要があります:

NSError *error;  
if (![self trySomethingWithError:&error]) {  
  // Handle Error  
}  

いけない:

NSError *error;  
[self trySomethingWithError:&error];  
if (error) {  
  // Handle Error  
}  

成功した場合、一部のApple APIはガベージ値(ガベージ値)を誤ったパラメーター(NULL以外の場合)に記録し、誤った値を判断すると偽の負の値が発生してクラッシュします。

シングルトンモード

シングルトンオブジェクトは、スレッドセーフモードを使用して共有インスタンスを作成する必要があります。

+ (instancetype)sharedInstance {  
  static id sharedInstance = nil;  
  static dispatch_once_t onceToken;  
  dispatch_once(&onceToken, ^{  
    sharedInstance = [[self alloc] init];  
  });  
  return sharedInstance;  
}  

これにより、起こりうる、場合によっては多発するクラッシュを防ぐことができます。

改行

スタイルガイドは主に印刷物とオンラインでの読みやすさを目的としているため、改行は重要なトピックです。

例えば:

self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];  

長いコード行は2行のコードに分割し、次の行は2つのスペースで区切る必要があります。

self.productsRequest = [[SKProductsRequest alloc]   
  initWithProductIdentifiers:productIdentifiers];  

Xcodeプロジェクト

ファイルの拡張を回避するために、物理ファイルはXcodeプロジェクトファイルと同期する必要があります。Xcodeグループの作成は、ファイルシステム内のファイルに反映される必要があります。コードはタイプごとにグループ化されているだけでなく、機能ごとにグループ化することもできるため、コードがより明確になります。

可能であれば、ターゲットのビルド設定で「警告をエラーとして扱う」をオンにし、次の追加の警告を有効にします。特別な警告を無視する必要がある場合は、Clangのプラグマ機能を使用します。

おすすめ

転載: blog.csdn.net/qq_27740983/article/details/50800759