Objective-Cで文字列定数を取り除く

コード内の魔法の定数は悪です。 コード内の文字列定数はさらに悪です。

そして、あなたは彼らからどこにも行けないようです、彼らはどこにでもいます:



1)xibsからオブジェクトをロードする場合:

MyView* view = [[[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil] lastObject];
      
      





 MyViewController* controller = [MyViewController initWithNibName:@"MyViewController" bundle:nil];
      
      





2)CoreDataを使用する場合:

 NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setEntity:[NSEntityDescription entityForName:@"MyCoreDataClass" inManagedObjectContext:moc]]; [request setSortDescriptors:@[ [[NSSortDescriptor alloc] initWithKey:@"someProperty" ascending:NO] ]];
      
      





3)KVOを使用する場合、次の行が表示されます。

 [self addObserver:someObservedObject forKeyPath:@"someProperty" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
      
      





4)さて、KVC:

 NSInteger maxValue = [[arrayOfMyClassObjects valueForKeyPath:@"@max.someProperty"] intValue];
      
      





5)ただし、CoreiteがSQLiteを直接使用することを好む場合でも、xibsを使用すると、このコードはおなじみのはずです。

 [self.tableView dequeueReusableCellWithIdentifier:@"MyTableViewCell"];
      
      





6)さて、AppleがStoryboardを世界に紹介したとき、それは素晴らしいことでした。

 [self performSegueWithIdentifier:@"MySegue" sender:nil]
      
      





 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:( id )sender { if ( [segue.identifier isEqual:@"MySegue"] ); }
      
      





問題がありますか? コンパイラは、行の内容を知らない(そして原則として知ることができない)ため、行の内容をチェックしません。 そして、自分自身を封印するか、xcdatamodel / xib / storyboard / renameプロパティの対応するフィールドの値を変更すると、コンパイル段階ではなく実行時にエラーが発生し、それをキャッチして修正するのに時間がかかり、より高価になります

それで、何ができるのでしょうか?

いくつかの行は管理手段によって処理でき、いくつかは特別なツールによって処理できます。



xibsからダウンロード


たとえば、xibの名前が含まれるクラスの名前と一致する必要があるというルールで開始する場合、最初の例のコードは次のように書き換えることができます。

 MyView* view = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([MyView class]) owner:self options:nil] lastObject];
      
      





 MyViewController* controller = [MyViewController initWithNibName:NSStringFromClass([MyViewController class] bundle:nil];
      
      





このソリューションの利点は、クラスの名前を変更することを決定した場合(Xcode->編集->リファクタリング->「関連ファイルの名前変更」チェックボックスを選択したまま名前を変更)、xibの名前を変更するときにも名前が変更され、それに応じて読み込みが行われることですビュー/コントローラーは影響を受けません。



コアデータ


例2では、​​ソリューションはもう少し複雑で複雑です。

まず、 MagicalRecordMogeneratorが必要です



CoreDataを使用していて、これらの優れたツールをまだ使用していない場合は、始めましょう。

MagicalRecordをポッドファイルに追加します(または、昔ながらの方法でファイルをプロジェクトにコピーします-詳細についてはgihabを参照してください)

 pod MagicalRecord
      
      





そして、発電機をインストールします。

 brew install mogenerator
      
      





または、サイトからインストーラーをダウンロードして、手動でインストールします。



mogeneratorは、CoreDataモデルファイルに基づいてソースファイルを作成します。そうでない場合は、手動で作成する必要があります。

次に、各ビルドでユーティリティを実行するようにプロジェクトを構成する必要があります。

プロジェクト->ターゲット->ビルドフェーズの追加->実行スクリプトの追加:

ターゲット設定、ビルドフェーズタブに移動し、スクリプトの形式で新しいフェーズを追加します。

画像

さて、スクリプト自体:

画像

モデルと同じレベルで、HumanとMachineの2つのフォルダーを作成する必要があります。 [CMD + B]をクリックします。その後、これら2つのフォルダーをプロジェクトに追加します。 CoreDataから生成されたモデルファイルが含まれます。



KVOおよびKVC


Objective-Cで文字列定数を広範囲に使用するもう1つのことは、KVOおよびKVCのKeyPathです。 上記のジェネレーターを使用できます。 モデルにMyCoreDataClassクラスがある場合、mogeneratorはMyCoreDataClassAttributes、MyCoreDataClassRelationships、およびMyCoreDataClassFetchedProperties構造を作成します。 そのため、例2を次のように書き換えることができます。

 NSArray* arr = [MyCoreDataClass MR_findAllSortedBy:MyCoreDataClassAttributes.someProperty ascending:NO inContext:moc];
      
      





また、例3は次のようになります。

 [self addObserver:myCoreDataClass forKeyPath:MyCoreDataClassAttributes.someProperty options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
      
      





ただし、このようなソリューションはCoreDataにのみ適しています。 もっと一般的なものが必要です。



Valid-KeyPathライブラリはこれに非常に適しています。

 #import "EXTKeyPathCoding.h" [self addObserver:myClass forKeyPath:KEY.__(MyClass, someProperty) options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
      
      





またはEXTKeyPathCoding ライブラリlibextobjc

 pod libextobjc
      
      





 #import "MTKValidKeyPath.h" [self addObserver:myClass forKeyPath:@keypath(MyClass.new, someProperty) options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
      
      





両方のソリューションの利点は、コード自体を作成する際のIDEからのオートコンプリートと、コンパイル段階でKeyPath自体をチェックすることです。



例4のようにKVCを使用する場合、KeyPathを生成するには、上記のライブラリを使用できます。または、 LinqToObjectiveCなど、Objective-CのLINQライクなライブラリを使用できます。

 pod LinqToObjectiveC
      
      





 NSInteger maxValue = [arrayOfMyClassObjects aggregate:^(MyClass* myClass, NSInteger aggregate){ return MAX( [myClass.someProperty intValue], aggregate); }];
      
      







絵コンテ


UI部分については、 ssgeneratorユーティリティが役立ちます。

プロジェクト用に別のスクリプトを作成します。

画像

次に、生成されたファイルStoryboardSegue.hおよびStoryboardSegue.mをプロジェクトに追加します。 これらのファイルには、識別子が定義されているUIStoryboardSegueまたはUITableViewCellを含むストーリーボード内のコントローラーのカテゴリが含まれます。 使用できるようになりました:

 [self.tableView dequeueReusableCellWithIdentifier:self.cell.MyTableViewCell];
      
      





 [self performSegueWithIdentifier:self.segue.MySegue sender:nil]
      
      





 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:( id )sender { if ( [segue.identifier isEqual:self.segue.MySegue] ); }
      
      







おわりに



コード内の文字列定数を取り除くこと自体は目的ではありませんが、コンパイル時のチェックによりコードの記述と維持を大幅に節約する方法です。 説明されているメソッドには、サードパーティのユーティリティが必要なものと、サードパーティのライブラリが必要なものがあります。 一部のプログラマーにとって、説明した手法は「複雑」で「読みにくい」ように見えますが、それらはすべて、コードのエラーをできるだけ早く特定することを目的としています。 したがって、変更しないコード、エラーを含まないコードを記述する場合、これらのメソッドは適切ではありません。



コード内の定数を個人的に取り除き、より柔軟でサポートしやすくするユーティリティとライブラリを念頭に置いている場合は、コメントに書いてください。このレビューに喜んで追加します。



All Articles