Objective-CとCocoaを記述するときに使用するベストプラクティスは何ですか?[閉まっている]


346

私はHIG(かなり便利です!)について知っていますが、Objective-Cを作成するとき、具体的にはCocoa(またはCocoaTouch)を使用するときに、どのプログラミングプラクティスを使用しますか。


このブログ投稿をご覧ください。ironwolf.dangerousgames.com/blog/archives/913
user392412

回答:


398

標準とは思えないことを始めました。

1)プロパティの出現により、「_」を使用して「プライベート」クラス変数のプレフィックスを付けることはなくなりました。結局のところ、他のクラスから変数にアクセスできる場合、そのプロパティはありませんか?私は常にコードを醜くするために "_"接頭辞を嫌っていましたが、今はそれを省くことができます。

2)プライベートなことについて言えば、私は.mファイル内のクラス拡張子のプライベートメソッド定義を次のように配置することを好みます。

#import "MyClass.h"

@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end

@implementation MyClass

部外者が気にする必要のないもので.hファイルを散らかすのはなぜですか?空の()は.mファイルのプライベートカテゴリに対して機能し、宣言されたメソッドを実装しない場合はコンパイル警告を発行します。

3)@synthesizeディレクティブのすぐ下の.mファイルの先頭にdeallocを配置することにしました。割り当て解除するものは、クラスで考えたいことのリストの一番上にあるべきではありませんか?これは、iPhoneなどの環境で特に当てはまります。

3.5)テーブルセルで、パフォーマンスのためにすべての要素(セル自体を含む)を不透明にします。つまり、すべてに適切な背景色を設定します。

3.6)NSURLConnectionを使用する場合、原則として、デリゲートメソッドを実装することをお勧めします。

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
      return nil;
}

ほとんどのWeb呼び出しは非常に特異であり、特にWebサービス呼び出しの場合は、応答をキャッシュするルールよりも例外です。示されているようにメソッドを実装すると、応答のキャッシュが無効になります。

また、興味深いのは、Joseph MattielloからのiPhone固有のヒント(iPhoneメーリングリストで受け取った)です。他にもありますが、これらは私が最も一般的に有用だと思いました(いくつかのビットがオリジナルから少し編集されて、応答で提供される詳細を含めるようになっていることに注意してください)。

4)CoreLocationを使用する場合など、必要な場合にのみ倍精度を使用します。定数を 'f'で終了し、gccに定数を浮動小数点数として格納させます。

float val = someFloat * 2.2f;

これはsomeFloat、実際にはdoubleの可能性がある場合に最も重要です。ストレージの「val」の精度が失われるため、混合モードの計算は必要ありません。iPhoneのハードウェアでは浮動小数点数がサポートされていますが、単精度ではなく倍精度演算を行うにはまだ時間がかかる場合があります。参照:

旧式の携帯電話では、計算はおそらく同じ速度で動作しますが、レジスタでは倍精度よりも単精度コンポーネントを多く使用できるため、多くの計算では単精度の方が高速になります。

5)プロパティをとして設定しますnonatomic。それらはatomicデフォルトであり、合成時に、マルチスレッドの問題を防ぐためにセマフォコードが作成されます。99%の人はおそらくこれを心配する必要はなく、コードを非アトミックに設定すると、肥大化が少なく、メモリ効率が高くなります。

6)SQLiteは、大きなデータセットをキャッシュする非常に高速な方法です。たとえば、マップアプリケーションはタイルをSQLiteファイルにキャッシュできます。最も高価な部分はディスクI / Oです。送信することにより、多くの小さな書き込みを避けるBEGIN;COMMIT;、大きなブロックの間。たとえば、新しい送信ごとにリセットする2秒のタイマーを使用します。有効期限が切れると、COMMITを送信します。これにより、すべての書き込みが1つの大きなチャンクに分けられます。SQLiteはトランザクションデータをディスクに保存します。これにより、Begin / Endラッピングを行うことで、多数のトランザクションファイルが作成されず、すべてのトランザクションが1つのファイルにグループ化されます。

また、SQLがメインスレッド上にある場合、GUIはブロックされます。クエリが非常に長い場合は、クエリを静的オブジェクトとして保存し、SQLを別のスレッドで実行することをお勧めします。クエリ文字列のデータベースを変更するものはすべて@synchronize() {}ブロックでラップしてください。短いクエリの場合は、便宜を図るためにメインスレッドに残すだけです。

SQLiteの最適化に関するその他のヒントがここにありますが、ドキュメントが古くなっているように見えますが、多くの点はおそらくまだ適切です。

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html


3
二重演算についての良いヒント。
Adam Ernst、

8
:クラスの拡張は現在、プライベートメソッドのための好ましい方法ですdeveloper.apple.com/Mac/library/documentation/Cocoa/Conceptual/...
Casebash

9
iPhone上のダブルスについてのあなたのアドバイスは、日付の外にあるstackoverflow.com/questions/1622729/...
Casebash

3
古くない。完全に間違っています。元のiPhoneは、ほぼ同じ速度でハードウェアでフロートとダブルをサポートしました。SQLiteはトランザクションをメモリに保持しません。それらはディスクにジャーナルされます。長いクエリのみがUIをブロックします。メインスレッドですべてを実行し、より高速なクエリを使用する方が煩雑ではありません。
tc。

1
@tc:トランザクションに関するSQL項目を修正しました。私自身、最後の4つほどの項目を記述していないことに注意してください。また、クエリをバックグラウンドに移動する部分は非常に長いクエリのみであることを明確にしました(たまに短くできない場合もあります)。しかし、いくつかの点のために全体を「間違っている」と呼ぶことは、私はかなり極端に感じます。また、上記の回答はすでに述べています:「おそらく古い電話では計算は同じ速度で動作します」しかし、単精度レジスタの数が多いのでそれらをより好ましいものにしている部分に注意してください。
Kendall Helmstetter Gelner、2010

109

不明な文字列をフォーマット文字列として使用しない

メソッドまたは関数がフォーマット文字列引数を取る場合、フォーマット文字列の内容を制御できることを確認する必要があります。

たとえば、文字列をログに記録するとき、文字列変数を唯一の引数としてに渡そうとするとNSLog

    NSString *aString = // get a string from somewhere;
    NSLog(aString);

この問題は、文字列にフォーマット文字列として解釈される文字が含まれている可能性があることです。これにより、誤った出力、クラッシュ、およびセキュリティの問題が発生する可能性があります。代わりに、文字列変数をフォーマット文字列に置き換える必要があります。

    NSLog(@"%@", aString);

4
私は以前これに噛まれたことがあります。
Adam Ernst、

これは、どのプログラミング言語にとっても良いアドバイスです
Tom Fobear '26

107

別の環境で慣れ親しんだものではなく、標準のCocoaの命名およびフォーマット規則と用語を使用します。ありますそこにココアの開発者の多くは、それらのもう一つは、あなたのコードで作業を開始したとき、それが見え、他のココアコードに似た感じならば、それははるかに親しみやすいでしょう。

何をすべきか、何をすべきでないかの例:

  • id m_something;オブジェクトのインターフェイスで宣言して、メンバー変数またはフィールドと呼ばないでください。somethingまたは_somethingをその名前に使用して、インスタンス変数と呼びます
  • ゲッターに名前を付けないでください-getSomething。ココアの正しい名前はただ-somethingです。
  • セッターに名前を付けないでください-something:。そのはず-setSomething:
  • メソッド名には引数が含まれ、コロンが含まれます。それは-[NSObject performSelector:withObject:]、ではありませんNSObject::performSelector
  • アンダーバー(アンダースコア)ではなく、メソッド名、パラメーター、変数、クラス名などでインターキャップ(キャメルケース)を使用します。
  • クラス名は大文字で始まり、変数とメソッド名は小文字で始まります。

他に何をするにしても、Win16 / Win32スタイルのハンガリー語表記を使用しないください。Microsoftでさえ、.NETプラットフォームへの移行でそれをあきらめました。


5
私は主張するでしょう、setSomething:/ somethingを使用しないでください-代わりにプロパティを使用してください。この時点で、実際にTigerをターゲットにする必要がある人はほとんどいない(プロパティを使用しない唯一の理由)
Kendall Helmstetter Gelner

18
プロパティは引き続きアクセサメソッドを生成し、プロパティのgetter = / setter =属性を使用してメソッドの名前を指定できます。さらに、プロパティでfoo.something構文の代わりに[foo something]構文を使用できます。そのため、アクセサーの命名は引き続き適切です。
Chris Hanson、

3
これは、あなたが反対することのほとんどを私がしたC ++から来た人にとっての素晴らしいリファレンスです。
クリントンブラックモア

4
セッターが何かをデータベースに保存させてはいけません。Core Dataが-save:メソッドを持っているのには理由があります。それは、setterが即座に更新を生成するのではなく、NSManagedObjectContextに対してメソッドです。
Chris Hanson、

2
それは選択肢ではなかったと思いますが、アプリのアーキテクチャを再検討する必要があったかもしれません。(明確にするために:「コアデータを使用する必要がありました。」とは言っていません。「セッターはデータベースに保存しないでください。」)オブジェクトグラフを個別に保存するのではなく、オブジェクトグラフを管理するためのコンテキストを持っている、事実上常に可能であり、より優れたソリューションです。
Chris Hanson

106

IBOutlets

歴史的に、アウトレットのメモリ管理は不十分でした。現在のベストプラクティスは、アウトレットをプロパティとして宣言することです。

@interface MyClass :NSObject {
    NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end

プロパティを使用すると、メモリ管理のセマンティクスが明確になります。また、インスタンス変数合成を使用する場合にも、一貫したパターンを提供します。


1
ニブのロードはそれを2回保持しませんか?(ペン先で1回、プロパティへの割り当てで2回目)。それらをdeallocで解放することになっていますか?
Kornel

6
リークを回避するには、viewDidUnload(iPhone OS 3.0+)またはカスタムのsetView:メソッドでアウトレットをnilする必要があります。もちろん、deallocでも解放する必要があります。
フランクシュツェルバ2009

2
誰もがこのスタイルに同意するわけではないことに注意してください
Michael

これはAppleが物事を行う方法でもあります。「iPhone 3開発の始まり」では、以前のバージョンからのこの変更についても言及しています。
ustun

これについては別のコメントで触れましたが、ここに配置する必要がありました。iOSアプリで動的ivar合成が開始されると(if /いつ?)、IBOutletをivarと比較してプロパティに配置できてうれしいです。
Joe D'Andrea

97

LLVM / Clang Static Analyzerを使用する

注:Xcode 4では、これはIDEに組み込まれています。

Clang Static Analyzerを使用して、当然のことながら、Mac OS X 10.5でCおよびObjective-Cコード(まだC ++なし)を分析します。インストールして使用するのは簡単です:

  1. このページから最新バージョンをダウンロードしてください
  2. コマンドラインcdからプロジェクトディレクトリへ。
  3. を実行しscan-build -k -V xcodebuildます。

(いくつかの追加の制約などがあります。特に、「デバッグ」構成でプロジェクトを分析する必要があります-詳細についてはhttp://clang.llvm.org/StaticAnalysisUsage.htmlを参照してください-しかし、それは多かれ少なかれ要約すると。)

次に、アナライザーは、コンパイラーが検出できない可能性のあるメモリー管理やその他の基本的な問題を示す一連のWebページを生成します。


1
:私はいくつかのトラブル私はこれらの指示に従ったまでの作業にこれを取得していたoiledmachine.com/posts/2009/01/06/...を
bbrown

15
Snow Leopard上のXCode 3.2.1では、それはすでに組み込まれています。[実行]-> [ビルドと分析]を使用して手動で実行するか、「静的アナライザーの実行」ビルド設定を介してすべてのビルドに対して有効にすることができます。このツールは現在、CおよびObjective-Cのみをサポートしており、C ++ / Objective-C ++はサポートしていないことに注意してください。
2009年

94

これは微妙ですが便利です。自分を別のオブジェクトへのデリゲートとして渡す場合は、の前にそのオブジェクトのデリゲートをリセットしてくださいdealloc

- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}

これにより、デリゲートメソッドが送信されなくなります。deallocエーテルに消えようとしているときに、偶然に何もメッセージを送信できないようにする必要があります。self.someObjectは、別のオブジェクト(シングルトンまたは自動解放プールなど)によって保持される可能性があり、「メッセージの送信を停止する!」と指示するまで、割り当てを解除しようとしているオブジェクトと見なされることに注意してください。公正なゲームです。

この習慣を身に付けることで、デバッグが面倒な多くの奇妙なクラッシュからあなたを救うでしょう。

同じ原則がKey Value ObservationとNSNotificationsにも適用されます。

編集:

さらに防御的に、変更:

self.someObject.delegate = NULL;

に:

if (self.someObject.delegate == self)
    self.someObject.delegate = NULL;

8
これについて微妙なことは何もありません。ドキュメントには、これを実行する必要があることが明確に記載されています。差出人Memory Management Programming Guide for CocoaAdditional cases of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates.
ジョン09/07/21

NULLはメモリを解放しないため、NULLの代わりにnilを使用することをお勧めします。
Naveen Shan

@NaveenShan nil == NULL。彼らは、それは除いて全く同じでnilあるidNULLされますvoid *。あなたの発言は真実ではありません。

@WTPはい、nil == NULLですが、nilの使用が明らかに推奨される方法です。リンゴのサンプルコードフラグメントを見ると、どこでもnilを使用しています。そして、あなたが言ったように、nilはidよりも優先されるため、void *よりも優先されます。 、IDを送信する場合、つまり。
Ahti

1
@Ahti正確で、Nil(大文字)は型Class*です。それらはすべて同じであるにもかかわらず、間違ったものを使用すると、特にObjective-C ++で厄介な小さなバグが発生する可能性があります。

86

@kendell

の代わりに:

@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end

使用する:

@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end

Objective-C 2.0の新機能。

クラス拡張については、AppleのObjective-C 2.0リファレンスで説明されています。

「クラス拡張により、プライマリクラス@interfaceブロック内以外の場所にあるクラスに必要な追加のAPIを宣言できます」

したがって、それらは実際のクラスの一部であり、クラスに加えて(プライベート)カテゴリではありません。微妙だが重要な違い。


あなたはそれを行うことができますが、私はそれを明示的に「プライベート」セクション(機能よりもドキュメント)としてラベル付けするのが好きですが、もちろん.mファイルにあることからすでに十分に明らかです...
Kendall Helmstetter Gelner

2
そこを除いてあるプライベートカテゴリとクラスの拡張機能の違いは:「次の例に示すように、クラスの拡張機能は、プライマリクラスの@interfaceブロック内以外の場所でのクラスのための追加の必要なAPIを宣言することができます:」編集中のリンクを参照してください。
schwa

CEメソッドを実装していない場合にコンパイラーが警告する違いがあることに同意します。ただし、すべてのメソッドが同じファイルにあり、すべてがプライベートである場合、その側面はそれほど重要ではありません。私は今でも前方参照ブロックをプライベートとしてマークするという保守性の面を好みます
Kendall Helmstetter Gelner

3
(Private)は()よりも保守しやすいとは思いません。あなたが心配しているなら、たくさんのコメントが役立つかもしれません。しかし、明らかに生きて生きさせましょう。YMMVなど
schwa

17
()代わりに(Private)(または他のカテゴリ名)を使用することにはかなり重要な利点があります。プロパティは読み取り専用として再公開できますが、一般には読み取り専用です。:)
Pascal、

75

自動解放を避ける

通常、(1)それらの存続期間を直接制御することはできないため、自動解放されたオブジェクトは比較的長期間持続し、アプリケーションのメモリフットプリントを不必要に増やす可能性があります。デスクトップではこれはほとんど影響がないかもしれませんが、より制約のあるプラットフォームでは、これは重大な問題になる可能性があります。したがって、すべてのプラットフォーム、特に制約の多いプラットフォームでは、自動解放オブジェクトにつながるメソッドの使用を避けることがベストプラクティスと見なされ、代わりにalloc / initパターンを使用することをお勧めします。

したがって、以下ではありません。

aVariable = [AClass convenienceMethod];

可能な場合は、代わりに次を使用する必要があります。

aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];

新しく作成されたオブジェクトを返す独自のメソッドを作成するときは、Cocoaの命名規則を利用して、メソッド名の先頭に「new」を付けることで、解放する必要があることをレシーバーに通知できます。

したがって、代わりに:

- (MyClass *)convenienceMethod {
    MyClass *instance = [[[self alloc] init] autorelease];
    // configure instance
    return instance;
}

あなたは書くことができます:

- (MyClass *)newInstance {
    MyClass *instance = [[self alloc] init];
    // configure instance
    return instance;
}

メソッド名は「new」で始まるため、APIのコンシューマーは、受け取ったオブジェクトを解放する責任があることを知っています(たとえば、NSObjectControllerのnewObjectmethodを参照)。

(1)独自のローカル自動解放プールを使用して制御できます。詳細については、「自動解放プール」を参照してください。


6
自動リリースを使用しないことの利点は、そのコストを上回る(つまり、メモリリークのバグが増える)ことに気づきました。とにかくメインスレッドのコードはかなり短時間で実行する必要があります(そうしないとUIがフリーズします)。実行時間が長くメモリを大量に消費するバックグラウンドコードの場合は、メモリを大量に消費する部分をローカルの自動解放プールに常にラップできます。
adib

56
同意しません。可能な限り、自動解放されたオブジェクトを使用する必要があります。それらがメモリフットプリントを過度に増加させる場合は、別のを使用する必要がありますNSAutoreleasePool。しかし、これが本当に問題であることを確認した後でのみ。時期尚早の最適化など…
Sven

3
40秒未満です。新しいオブジェクトをインスタンス化するときに[someObject release]と入力して「余分な行」を読む一日でしたが、特別な場合にのみ表示され、コンソールに一貫したエラーが発生しない自動リリースのバグを見つけるのに17時間もかかりました。したがって、「自動リリースを使用しないことの利点はそのコストよりも大きい」と彼が言うとき、私はadibに同意します。
RickiG、

7
私はスヴェンに同意します。主な目標は、コードの明快さとコーディングエラーの削減であり、必要な場合にのみメモリを最適化する必要があります。[[[Foo alloc] init] autorelease]をタイプアウトするのは簡単で、この新しいオブジェクトを解放する問題にすぐに対処できます。コードを読むとき、対応するリリースを探し回って、リークされていないことを確認する必要はありません。
マイクウェラー

3
自動解放されたオブジェクトのライフサイクルは十分に定義され、十分なレベルで決定可能です。
Eonil、2011年

69

これらのいくつかはすでに言及されていますが、ここで私が頭のてっぺんから考えることができるものは次のとおりです。

  • KVOの命名規則に従います。あなたが今KVOを使用していなくても、私の経験では、多くの場合、将来的にはまだ有益です。また、KVOまたはバインディングを使用している場合は、想定どおりに機能していることを知る必要があります。これは、アクセサーメソッドとインスタンス変数だけでなく、多対多の関係、検証、依存キーの自動通知などもカバーしています。
  • カテゴリにプライベートメソッドを配置します。インターフェースだけでなく、実装も同様です。プライベートメソッドと非プライベートメソッドの間に概念的に距離を置くことは良いことです。すべてを.mファイルに含めます。
  • カテゴリにバックグラウンドスレッドメソッドを配置します。同上。メインスレッドに何があり、何がそうでないかについて考えているときは、明確な概念上の障壁を維持するのが良いと思いました。
  • を使用し#pragma mark [section]ます。通常、私は自分のメソッド、各サブクラスのオーバーライド、および情報または正式なプロトコルごとにグループ化します。これにより、まさに私が探しているものにジャンプするのがずっと簡単になります。同じトピックで、類似のメソッド(テーブルビューのデリゲートメソッドなど)をグループ化し、どこかに貼り付けないでください。
  • プライベートメソッドとivarの前に_を付けます。私はそれがどのように見えるかが好きで、私が誤ってプロパティを意味するときは、ivarを使用する可能性は低くなります。
  • initおよびdeallocでミューテーターメソッド/プロパティを使用しないでください。そのために何か悪いことが起こったことは一度もありませんが、オブジェクトの状態に応じて何かを行うようにメソッドを変更すると、ロジックを確認できます。
  • プロパティにIBOutletsを配置します。私は実際にこれをここで読んだだけですが、これから始めます。メモリの利点に関係なく、(少なくとも私にとっては)文体的にはより良いようです。
  • 絶対に必要ではないコードを記述しないでください。これは、a #defineが行うときにivarを作成する、データが必要になるたびに配列をソートする代わりに配列をキャッシュするなど、多くのことを実際にカバーしています。これについて私が言えることはたくさんありますが、最終的には、必要になるか、プロファイラーから指示されるまで、コードを記述しないでください。長期的に見ると、メンテナンスがずっと簡単になります。
  • あなたが始めることを終えなさい。半完成したバグの多いコードがたくさんあるのが、プロジェクトを死に至らしめる最速の方法です。問題のないスタブメソッドが必要な場合は、NSLog( @"stub" )内部に置くことでそれを示すか、または追跡しておきます。

3
クラスの継続にプライベートメソッドを配置することをお勧めします。(つまり@interface MyClass()... @end in your .m)
Jason Medeiros

3
#PRAGMAの代わりに、コメント//マーク:[セクション]を使用できます。これは、移植性が高く、同じように機能します。
aleemb 2009年

欠けている特別な構文がない限り、//マーク:Xcodeの関数ドロップダウンメニューにラベルを追加しません。これは実際に使用する理由の半分です。
マークシャル

6
ドロップダウンに表示するには、大文字の「// MARK:...」を使用する必要があります。
Rhult、2009年

3
に関しては、ドロップダウンに表示される完了のコードをマークFinish what you startする// TODO:ためにも使用できます。
iwasrobbされた2010

56

単体テストを記述します。Cocoaでは、他のフレームワークでは難しいかもしれない多くのことをテストできます。たとえば、UIコードを使用すると、通常、接続されていることを確認し、使用時に機能することを信頼できます。また、状態を設定し、デリゲートメソッドを簡単に呼び出してテストできます。

また、内部メソッドのテストを作成するときに邪魔になる、パブリックメソッドとプロテクトメソッドとプライベートメソッドの可視性もありません。


どのテストフレームワークをお勧めしますか?
melfar 2009年

13
Xcodeには、Objective-CユニットテストフレームワークであるOCUnitと、ビルドプロセスの一部としてユニットテストのバンドルを実行するためのサポートが含まれています。
Chris Hanson、

55

黄金律:もしあなたがallocそれならあなたはrelease

更新:ARCを使用していない場合


26
また、あなたの場合copymutableCopynewまたはretain
Sven

54

Java / C#/ C ++ / etcのようにObjective-Cを記述しないでください。

Java EE Webアプリケーションの作成に慣れているチームがCocoaデスクトップアプリケーションを作成しようとしているのを見たことがあります。Java EE Webアプリケーションであるかのように。FooクラスとおそらくFooableプロトコルだけが必要なときに、たくさんのAbstractFooFactoryとFooFactory、IFooとFooが飛び交っていました。

これを行わないようにすることの一部は、言語の違いを本当に理解することです。たとえば、Objective-Cクラスのメソッドはインスタンスメソッドと同じように動的にディスパッチされ、サブクラスでオーバーライドできるため、上記の抽象ファクトリおよびファクトリクラスは必要ありません。


10
Objective-Cで抽象的なファクトリを作成したJava開発者として、私はこれに興味をそそられます。これがどのように機能するかをもう少し説明していただけませんか-例を挙げてください。
ティーボット2009

2
あなたがこの回答を投稿して以来、過去のすべての時間が経過した後も、抽象ファクトリクラスが不要であるとまだ信じていますか?
kirk.burleson 2010

50

必ずDebugging Magicページをブックマークしてください。これは、Cocoaバグの原因を見つけようとしているときに、壁に頭をぶつけたときの最初のストップです。

たとえば、後でアプリを終了するときなどに、後でクラッシュを引き起こしているメモリを最初に割り当てたメソッドを見つける方法がわかります。


1
Debugging MagicページのiOS固有のバージョンがあります。
Jeethu 2011

38

私がNewbiecategoryaholismと呼ぶことにしたものを避けてください。Objective-Cの初心者がカテゴリを発見すると、それらはしばしば乱暴になり、存在するすべてのクラスに便利な小さなカテゴリを追加します「何ですか?数値をローマ数字に変換してNSNumberに変換するメソッドを追加できます!」)。

これを行わないでください。

コードはより移植性が高く、理解しやすくなります。ダースクラスを20ダース上に分散させた、数十の小さなカテゴリメソッドが必要になります。

ほとんどの場合、コードを合理化するためにカテゴリメソッドが本当に必要だと思うときは、メソッドを再利用することはありません。

カテゴリメソッドを名前空間に使用している場合を除いて、他にも危険があります(完全に異常なddribin以外は誰ですか?)アドレススペースで実行されているApple、プラグイン、またはその他のものも同じカテゴリを定義する可能性があります。同じ名前のメソッドで、副作用が少し異なります...

OK。警告されたので、「この部分を実行しない」を無視してください。しかし、極端な拘束を行使してください。


私はあなたの答えが好きです。いくつかのコードを複数の場所で複製しようとし、コードが分類しようとしているクラスにコードが明確に属していない限り、カテゴリを使用してユーティリティコードを保存しないでください...
Kendall Helmstetterゲルナー

パイプラインを使用して、名前空間カテゴリのメソッドに対するサポートを表明したいと思います。それはまさに正しいことのようです。
Michael Buckley、

ローマ数字のみの場合は+1。私はそれを完全にやります!
Brian Postow、2010年

14
対置:過去1年半の間、私は正反対の方針に従いました。「カテゴリに実装できる場合は、そうします。」その結果、私のコードは、Appleが提供する詳細なサンプルコードよりもはるかに簡潔で表現力があり、読みやすくなっています。1つの名前空間の競合により、合計で約10分を失いました。おそらく、私が自分で作成した効率から数か月を稼いだことでしょう。一人一人に、しかし私はリスクを知ってこの方針を採用しました、そして私がやったことは非常にうれしいです。
cduhn 2010

7
同意しません。それが関数になり、Foundationオブジェクトに適用され、適切な名前が考えられる場合は、カテゴリーに入れます。コードが読みやすくなります。ここでの重要なポイントは、すべてを適度に行うことです。
mxcl、2010年

37

世界のサブクラス化に抵抗する。Cocoaでは、他のフレームワークではサブクラス化を介して行われる、基本的なランタイムの委任と使用によって多くのことが行われます。

たとえば、Javaでは匿名*Listenerサブクラスのインスタンスを多く使用し、.NETではEventArgsサブクラスを多く使用します。Cocoaではどちらもしません—代わりにtarget-actionが使用されます。


6
それ以外の場合は「継承を介した構成」として知られています。
Andrew Ebling

37

ユーザーが望むように文字列をソートする

文字列を並べ替えてユーザーに提示するときは、単純なcompare:方法を使用しないでください。代わりに、localizedCompare:またはなどのローカライズされた比較メソッドを常に使用する必要がありますlocalizedCaseInsensitiveCompare:

詳細については、「文字列の検索、比較、および並べ替え」を参照してください。


31

宣言されたプロパティ

通常、すべてのプロパティに対してObjective-C 2.0で宣言されたプロパティ機能を使用する必要があります。それらがパブリックでない場合は、クラス拡張に追加します。宣言されたプロパティを使用すると、メモリ管理のセマンティクスがすぐに明確になり、deallocメソッドを確認しやすくなります。プロパティ宣言をグループ化すると、それらをすばやくスキャンして、deallocメソッドの実装と比較できます。

プロパティを「非アトミック」としてマークしない場合は、よく考える必要があります。言語ガイドのプログラミングのObjective Cのノートを、プロパティは、デフォルトでは、原子であり、負担はかなりのオーバーヘッド。さらに、すべてのプロパティをアトミックにするだけでは、アプリケーションがスレッドセーフになるわけではありません。もちろん、 'nonatomic'を指定せずに独自のアクセサメソッドを実装する場合(それらを合成するのではなく)、アトミックな方法で実装する必要があることにも注意してください。


26

nil値について考える

この質問のノート、メッセージがするnilObjective-Cで有効です。これは多くの場合利点ですが、よりクリーンでより自然なコードにつながりますが、この機能nilは、予期しない値を取得した場合に、奇妙で追跡が難しいバグにつながる場合があります。


私はこれを持っている:#define SXRelease(o); o = nilとで同じCFReleasefree。これはすべてを簡素化します。

26

NSAssertとその仲間を使用します。私は常にnilを有効なオブジェクトとして使用しています...特にnilへのメッセージの送信はObj-Cでは完全に有効です。ただし、変数の状態を本当に確認したい場合は、NSAssertとNSParameterAssertを使用します。これにより、問題を簡単に追跡できます。



23

シンプルだが忘れられがちなもの。仕様によると:

一般に、同じセレクター(同じ名前)を持つ異なるクラスのメソッドも、同じ戻り値と引数の型を共有する必要があります。この制約は、動的バインディングを可能にするためにコンパイラーによって課されます。

その場合、同じ名前のセレクターはすべて、たとえ異なるクラスあっても、戻り値/引数の型が同じであると見なされます。これは簡単な例です。

@interface FooInt:NSObject{}
-(int) print;
@end

@implementation FooInt
-(int) print{
    return 5;
}
@end

@interface FooFloat:NSObject{}
-(float) print;
@end

@implementation FooFloat
-(float) print{
    return 3.3;
}
@end

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
    id f1=[[FooFloat alloc]init];
    //prints 0, runtime considers [f1 print] to return int, as f1's type is "id" and FooInt precedes FooBar
    NSLog(@"%f",[f1 print]);

    FooFloat* f2=[[FooFloat alloc]init];
    //prints 3.3 expectedly as the static type is FooFloat
    NSLog(@"%f",[f2 print]);

    [f1 release];
    [f2 release]
    [pool drain];

    return 0;
}   

忘れがちです。それでも重要
Brock Woolf

3
これは、静的型付けを控える場合にのみ問題になります。コンパイラが型を知っている場合、引数と戻り値の型は問題なく異なる可能性があります。個人的には、これが問題になることはあまりありません。Appleには、同じ名前で戻り値の型が異なる多くのメソッドもあります。最後に、あいまいな場合に警告するコンパイラフラグがあります。
Nikolai Ruhe

Appleの命名規則ガイドラインに従えば、この状況は起こりません:)
Eonil

22

Leopard(Mac OS X 10.5)以降を使用している場合は、Instrumentsアプリケーションを使用してメモリリークを検出および追跡できます。Xcodeでプログラムをビルドした後、[実行]> [パフォーマンスツールで開始]> [リーク]を選択します。

アプリでリークが表示されない場合でも、オブジェクトを長時間保持している可能性があります。Instrumentsでは、これにObjectAllocインストゥルメントを使用できます。InstrumentsドキュメントでObjectAllocインストゥルメントを選択し、[表示]> [詳細]を選択して、インストルメントの詳細を表示します(まだ表示されていない場合)(横にチェックマークが付いているはずです)。ObjectAllocの詳細の[Allocation Lifespan]で、[Created&Still Living]の横にあるラジオボタンを必ず選択してください。

アプリケーションの記録を停止するときはいつでも、ObjectAllocツールを選択すると、「#Net」列に、アプリケーション内の各静止オブジェクトへの参照の数が表示されます。自分のクラスだけでなく、NIBファイルの最上位オブジェクトのクラスも確認してください。たとえば、画面にウィンドウがなく、まだ生きているNSWindowへの参照が表示されている場合、それをコードで解放していない可能性があります。


21

deallocでクリーンアップします。

これは忘れることが最も簡単なことの1つです-esp。150mphでコーディングするとき。常に、常に、常にdeallocで属性/メンバー変数をクリーンアップします。

-私はにObjC 2の属性を使用したい、これはクリーンアップ無痛になりますので、 -新しいドット表記を。多くの場合、次のように単純です。

- (void)dealloc
{
    self.someAttribute = NULL;
    [super dealloc];
}

これでリリースが処理され、属性がNULLに設定されます(防御プログラミングを検討します-deallocでさらに別のメソッドが再度メンバー変数にアクセスする場合-まれですが、発生する可能性があります)。

10.5でGCをオンにすると、これはそれほど必要なくなります。ただし、作成した他のリソースをクリーンアップする必要がある場合は、代わりにfinalizeメソッドで実行できます。


12
一般に、dealloc(またはinit)ではアクセサメソッドを使用しないでください。
mmalc 2008年

1
パフォーマンス上の理由(アクセサは直接アクセスよりも少し遅い)を除いて、なぜdeallocやinitでアクセサを使用すべきではないのですか?
schwa

1
(a)パフォーマンス上の理由は、それ自体が完全に適切な理由です(特に、アクセサーがアトミックな場合)。(b)アクセサーが持つ可能性のある副作用を回避する必要があります。後者は、クラスがサブクラス化されている場合に特に問題になります。
mmalc 2008年

3
合成されたivarを使用して最新のランタイムで実行している場合、deallocでアクセサーを使用する必要があることに注意します。最新のランタイムコードの多くはGCですが、すべてではありません。
Louis Gerbarg、2008年

1
アクセサメソッド/プロパティを使用するかどうか、-initおよび-deallocメソッドを使用するかどうかのより詳細なビューは、mikeash.com
Johan Kool、

17

これらのコメントはすべてすばらしいですが、しばらく前に公開されたGoogleのObjective-Cスタイルガイドについて誰も触れなかったことに本当に驚いています。彼らは非常に徹底した仕事をしたと思います。


7
うーん、最初の例はすでにでたらめでいっぱいです。言語イディオムを文書化しないでください。ヘッダーファイルでそのような種類のコメントを見つけたとしても、読み続ける必要はありません。
Stephan Eggermont 2010

5
ああ目!見たものが信じられない。
Eonil、2011年


13

NSWindowControllerとNSViewControllerは、管理するNIBファイルの最上位オブジェクトを解放することを忘れないでください。

NIBファイルを手動でロードする場合は、NIBの最上位オブジェクトを使い終わったら、そのオブジェクトを解放する必要があります。


12

初心者が使用するかなり明白な1つ:コードにXcodeの自動インデント機能を利用します。別のソースからコピー/貼り付けしている場合でも、コードを貼り付けたら、コードのブロック全体を選択して右クリックし、そのブロック内のすべてをインデントし直すオプションを選択できます。

Xcodeは実際にそのセクションを解析し、ブラケット、ループなどに基づいてインデントします。各行ごとにスペースバーまたはタブキーを押すよりもはるかに効率的です。


Tabをインデントに設定して、Cmd-AとTabを実行することもできます。
Plumenator 2010

10

最初にCocoaプログラミングを始めたとき、これを見落としていたことを知っています。

NIBファイルに関するメモリ管理の責任を理解していることを確認してください。ロードするNIBファイル内の最上位オブジェクトを解放する必要があります。この件に関するAppleのドキュメントを読んでください。


6
本当じゃない。トップレベルのオブジェクトをリリースする責任があるかどうかは、継承元のクラスと使用しているプラ​​ットフォームによって異なります。とりわけdeveloper.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…を参照てください。
mmalc 2008年

10

すべてのGCC警告をオンにしてから、Appleのヘッダーによって定期的に発生する警告をオフにして、ノイズを減らします。

また、Clang静的分析を頻繁に実行します。「スタティックアナライザーの実行」ビルド設定を使用して、すべてのビルドで有効にすることができます。

単体テストを作成し、ビルドごとに実行します。


そして、可能であれば、「警告をエラーとして扱う」をオンにします。警告が出ないようにします。
Peter Hosey、

2
推奨される警告を使用してプロジェクトを設定するための便利なスクリプトは、こちらから入手できます:rentzsch.tumblr.com/post/237349423/hoseyifyxcodewarnings-scpt
Johan Kool

10

変数とプロパティ

1 /ヘッダーをクリーンに保ち、実装を
非表示にするヘッダーにインスタンス変数を含めないでください。プロパティとしてクラス継続に配置されるプライベート変数。パブリック変数は、ヘッダーでパブリックプロパティとして宣言します。読み取り専用にする必要がある場合は、読み取り専用として宣言し、クラスの継続でreadwriteとして上書きします。基本的に私は変数をまったく使用せず、プロパティのみを使用しています。

2 /プロパティにデフォルト以外の変数名を付けます。例:


@synthesize property = property_;

理由1:「自己」を忘れることによって引き起こされるエラーをキャッチします。プロパティを割り当てるとき。理由2:私の実験から、InstrumentsのLeak Analyzerは、デフォルト名でリークするプロパティを検出するのに問題があります。

3 /プロパティで直接保持または解放を使用しないでください(または非常に例外的な状況でのみ)。deallocでそれらにnilを割り当てるだけです。保持プロパティは、保持/解放を単独で処理するためのものです。たとえば、セッターがオブザーバーを追加または削除していないかどうかはわかりません。変数は、セッターとゲッターの内部でのみ直接使用する必要があります。

ビュー

1 /可能であれば、すべてのビュー定義をxibに配置します(例外は通常、動的コンテンツとレイヤー設定です)。時間を節約し(コードを書くより簡単です)、変更が簡単で、コードをクリーンに保ちます。

2 /ビューの数を減らしてビューを最適化しようとしないでください。コードにサブビューを追加したいという理由だけで、xibの代わりにUIImageViewを作成しないでください。代わりに、UIImageViewを背景として使用してください。ビューフレームワークは何百ものビューを問題なく処理できます。

3 / IBOutletsは、常に保持(または強力)する必要はありません。ほとんどのIBOutletはビュー階層の一部であるため、暗黙的に保持されることに注意してください。

4 / viewDidUnloadのすべてのIBOutletを解放する

5 / deallocメソッドからviewDidUnloadを呼び出します。暗黙的に呼び出されることはありません。

記憶

1 /作成時にオブジェクトを自動解放します。多くのバグは、リリースコールを1つのif-elseブランチに移動するか、returnステートメントの後で発生します。自動リリースではなくリリースは、例外的な状況でのみ使用する必要があります。たとえば、実行ループを待機していて、オブジェクトの自動リリースが早すぎたくない場合などです。

2 / Authomatic Reference Countingを使用している場合でも、retain-releaseメソッドの仕組みを完全に理解する必要があります。手動で保持リリースを使用することは、ARCほど複雑ではありません。どちらの場合も、リークと保持サイクルについて考慮する必要があります。大きなプロジェクトや複雑なオブジェクト階層では、retain-releaseを手動で使用することを検討してください。

コメント

1 /コードを自動ドキュメント化します。すべての変数名とメソッド名は、それが何をしているかを伝える必要があります。コードが正しく記述されている場合(これには多くの練習が必要です)、コードのコメントは必要ありません(ドキュメントのコメントと同じではありません)。アルゴリズムは複雑になる可能性がありますが、コードは常に単純でなければなりません。

2 /時には、コメントが必要になることがあります。通常は、明らかでないコードの動作やハックを説明します。コメントを書く必要があると思われる場合は、最初にコードを書き直して、コメントを必要としないようにしてください。

インデント

1 /インデントを大きくしすぎないでください。ほとんどのメソッドコードは、メソッドレベルでインデントする必要があります。ネストされたブロック(もしあれば)は、読みやすさを低下させます。ネストされたブロックが3つある場合は、内部ブロックを別のメソッドに配置する必要があります。4つ以上のネストされたブロックは使用しないでください。メソッドコードの大部分がif内にある場合は、if条件を否定します。次に例を示します。


if (self) {
   //... long initialization code ...
}

return self;

if (!self) {
   return nil;
}

//... long initialization code ...

return self;

Cコード、主にC構造体を理解する

Obj-Cは、C言語上の軽いOOPレイヤーにすぎないことに注意してください。Cの基本的なコード構造(列挙型、構造体、配列、ポインタなど)がどのように機能するかを理解する必要があります。例:


view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height + 20);

と同じです:


CGRect frame = view.frame;
frame.size.height += 20;
view.frame = frame;

などなど

独自のコーディング標準ドキュメントを作成し、頻繁に更新します。バグから学ぶようにしてください。バグが作成された理由を理解し、コーディング標準を使用してバグを回避してください。

現在、コーディング標準には約20ページがあり、Javaコーディング標準、Google Obj-C / C ++標準、および独自の追加が混在しています。コードを文書化し、標準の標準インデント、空白、空白行を適切な場所に使用します。


9

より機能的になります

Objective-Cはオブジェクト指向言語ですが、Cocoaフレームワークは機能的なスタイルを認識しており、多くの場合、機能的なスタイルで設計されています。

  1. 可変性の分離があります。不変クラスをプライマリとして、可変オブジェクトをセカンダリとして使用します。たとえば、主にNSArrayを使用し、必要な場合にのみNSMutableArrayを使用します。

  2. 純粋な機能があります。それほど多くはありませんが、純粋な関数のように設計されたフレームワークAPIの多くを購入してください。CGRectMake()またはなどの関数を見てくださいCGAffineTransformMake()。明らかに、ポインタ形式はより効率的に見えます。ただし、ポインタを使用した間接的な引数は、副作用のないものにはなりません。構造をできるだけ純粋に設計します。状態オブジェクトを分離します。他のオブジェクトに値を渡すときの-copy代わりに使用し-retainます。共有された状態は、他のオブジェクトの値への変更に静かに影響を与える可能性があるためです。だから、副作用のないことはできません。オブジェクトの外部からの値がある場合は、それをコピーします。したがって、共有状態を最小限に抑えることも重要です。

ただし、不純な関数の使用も恐れないでください。

  1. 怠惰な評価があります。-[UIViewController view]プロパティのようなものを参照してください。オブジェクトが作成されても、ビューは作成されません。発信者がview最初にプロパティを読んだときに作成されます。UIImage実際に描画されるまで読み込まれません。この設計のような多くの実装があります。この種の設計はリソース管理に非常に役立ちますが、遅延評価の概念を知らなければ、それらの動作を理解するのは簡単ではありません。

  2. 閉鎖があります。Cブロックをできるだけ使用します。これはあなたの人生を大幅に簡素化します。ただし、使用する前に、ブロックメモリ管理についてもう一度お読みください。

  3. セミオートGCがあります。NSAutoreleasePool。-autoreleaseプライマリを使用します。-retain/-release本当に必要な場合は、手動セカンダリを使用してください。(例:メモリの最適化、明示的なリソースの削除)


2
3)に関しては、私は反対のアプローチを提案します:可能な限り手動の保持/解放を使用してください!このコードがどのように使用されるかは誰にもわかりません。タイトなループで使用されると、メモリ使用量が不必要に増大する可能性があります。
Eiko、

@Eikoこれは時期尚早の最適化であり、一般的なガイダンスにはなりません。
Eonil、

1
特にモデルクラスで作業する場合は、設計上の問題だと思います。私は副作用としてメモリの増加を考えていますが、それは私が頻繁に表示したいものではありません。さらに悪いことに、私のコードを使用する別の開発者は、高価な呼び出しを自動解放プールにラップするしかありません(できれば、オブジェクトが他のライブラリコードに送信される可能性があります)。そして、それらの問題を後で診断することは困難ですが、そもそも回避するのは安価です。渡されたオブジェクトをコピー/自動解放する場合、それらが予想よりもはるかに大きいと、失われる可能性があります。ただし、GUIコードの方がリラックスしています。
栄子

@Eiko私autoreleaseはメモリを一般的に長く保持することに同意し、マニュアルretain/releaseはケースのメモリ消費を減らすことができます。しかしそれは、特殊なケースの最適化のための指針(も、あなたがいつも感じている!)あるべきな時期尚早な最適化を一般化する理由にはできません練習。そして実際、あなたの提案は私と反対ではありません。私はそれが本当に必要な場合として言及しました:)
Eonil
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.