読みやすくするために、Objective-Cコードを複数のファイルに分割する必要があると感じることがよくあります。クラスを作るのを避けて呼びたいです。単純なインポートが必要です(phpのように)。
誰かが実際の例を参照していただければ幸いです。
読みやすくするために、Objective-Cコードを複数のファイルに分割する必要があると感じることがよくあります。クラスを作るのを避けて呼びたいです。単純なインポートが必要です(phpのように)。
誰かが実際の例を参照していただければ幸いです。
この場合、カテゴリを見ていると思います。
あなたがしなければならないのは、新しい.h.mペアを.hファイルに作成することだけです。
#import MyClass.h
@interface MyClass(Networking)
//method declarations here
@end
および.mファイル内:
#import MyClass+Networking.h
@implementation MyClass(Networking)
//method definitions here
@end
そして、MyClass.mファイルで-#import MyClass + Networking.hを実行すると、すべて設定されます。このようにして、クラスを拡張できます。
あなたは「クラスを作るのを避けて、それらを呼びたい」と言います。クラスを追加することへの恐れを克服する必要があります。クラスの実装を複数のファイルに分割する必要があると感じた場合は、1つのクラスで多くのことを実行しようとしている可能性があります。そのクラスに他のクラスへのいくつかの責任を引き継ぐ(「委任する」)ようにする必要があります。
とはいえ、クラスの実装を分割する方法はいくつかあります。肥大化したクラスの設計を修正する以外のより良い方法は、カテゴリまたはクラス拡張を使用することです。カテゴリと拡張機能については、Objective-Cプログラミング言語ですべて読むことができます。リンカは実行可能ファイルを作成するときにカテゴリと拡張機能をクラスにマージするため、独自のクラスでカテゴリまたは拡張機能を使用しても実行時のペナルティはありません。
さらに悪い方法は、Cプリプロセッサの#include
ディレクティブを使用して複数のファイルを一緒に貼り付けることです。実装ファイルからいくつかのメソッドを取り出して、それらを新しい「フラグメント」ファイルに貼り付け、次に#include
フラグメントファイルを実装ファイルに貼り付けることができます。 これを行うと、ソースコードを理解するのが難しくなります。 これを行うことはお勧めしませんが、とにかく例を示します。
#import "MyObject.h"
@implementation MyObject
- (void)aMethod { ... }
#include "MyObject-moreMethods.m"
@end
// Note: do not include this target in the “Compile Sources” build phase of your target.
// And **NO** @implementation statement here!
- (void)methodTwo { ... }
- (void)methodThree { ... }
[編集/更新-他のいくつかの回答で言及されているように、カテゴリを使用するために、以下で説明するアプローチを放棄しました。私の典型的な状況は、ビューにコントロールコンポーネントが追加されているため、コントロールのさまざまなデリゲートおよびデータソースメソッドに対応するコードが追加されると、ViewControllerファイルが扱いにくくなることです。次に、View Controllerファイルにコードスタブを追加し、それらをクラスカテゴリに実際に実装します。たとえば、検索バーとコレクションビューを備えたAlbumViewController画面がある場合、AlbumViewController+SearchBarカテゴリとAlbumViewController+CollectionViewカテゴリを作成します。これにより、含まれているファイルについて以下にリストする欠点を発生させることなく、ViewControllerクラスを適切なサイズに保つことができます。唯一の欠点は、インスタンス変数、つまりプロパティ、
また、ファイルを分割したいのですが、カテゴリが正しい解決策ではない場合があると思います。たとえば、ペン先が複数のオブジェクト(テーブル、ボタン、ナビゲーションバー、タブなど)を含むようになると、viewcontroller.mファイルにすべてのサポートメソッドを配置する必要があるため、非常に大きくて扱いにくいファイルになる可能性があります。
この説明では、元の/標準の.mファイルを親として参照し、補助的な.mファイルを子として参照します。
目標は、単一の親.hと.m、およびそれぞれが個別に編集できる複数の子.mファイルを持つことですが、すべての子.mファイルが親.mファイルにあるかのようにコンパイルされます。
これは、たとえば、すべてのテーブル関連メソッドをファイルに配置して編集し、viewcontroller.m実装ファイルに含まれているかのようにコンパイルする機能が必要な場合に役立ちます。これは可能のようですが、少しの努力が必要であり、1つの(おそらく深刻な)欠点があります。
覚えておくべきことは、2つの異なるツールが使用されていることです。IDE(インテリジェントなソース編集を提供する)と、プロジェクトのソースコードを実行可能なアプリに変換するコンパイラ/makeシステムです。
IDEを期待どおりに機能させるには、子ファイルがクラスの実装の一部であるように見える必要があります。これは、@implementationディレクティブと@endディレクティブを条件付きコンパイルマクロでラップすることで実現できます。ファイルを単独で編集する場合、IDEは子ファイルをクラスの本体と見なしますが、コンパイラはそうではありません。
コンパイラに文句を言わないようにするために、子ファイルをターゲットの一部と見なすことはできません。代わりに、プリプロセッサの#includeディレクティブを介して子ファイルをプルします。これは、ファイルの作成時(またはプロジェクトへの追加時)にターゲットに追加しないか、[ビルドフェーズ]->[ソースのコンパイル]ペインで削除することで実現できます。
次に、親の.mファイルの本文内に子の.mファイルを#includeします。コンパイラはそれらを「インプレース」でロードし、文句なしに必要に応じてソースをコンパイルします。
このアプローチの欠点は(これまでのところ)、デバッガーが子メソッドを認識せず、それらに設定されたブレークポイントでブレークしないことです。そのため、このアプローチは、コードを徹底的にテストした後、またはテーブルデリゲートやデータソースメソッドなど、比較的些細でよく知られているコードチャンクに対してのみ使用することをお勧めします。
これは、ペン先にテーブルとテキストフィールドがあり、子.mファイルで定義されたサポートデリゲートメソッドを持つプロジェクトの.hファイルと.mファイルです。ペン先では、インターフェイスオブジェクトは正常に配線され、デリゲートがファイル所有者に設定されています。
ファイル(親) "MyViewController.h":
#import <UIKit/UIKit.h>
@interface MyViewController : UIViewController
@property (retain, nonatomic) IBOutlet UITableView *myTable;
@property (retain, nonatomic) IBOutlet UITextField *myTextField;
@end
ファイル(親)MyViewController.m:
#import "MyViewController.h"
#define VIEW_CONTROLLER_MAIN_BODY 1
@interface MyViewController ()
@end
@implementation MyViewController
#include "MyViewController_TableMethods.m"
#include "MyViewController_TextFieldMethods.m"
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
[_myTable release];
[_myTextField release];
[super dealloc];
}
ファイル(子)MyViewController_TableMethods.m:
#import <UIKit/UIKit.h>
#import "MyViewController.h"
#ifndef VIEW_CONTROLLER_MAIN_BODY
@implementation ViewController
#endif
#pragma mark -
#pragma mark Table View Common Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
static NSString *myIdentifier = @"myCellIdentifier";
static UITableViewCellStyle myStyle = UITableViewCellStyleSubtitle;
UITableViewCell *cell = [self.myTable dequeueReusableCellWithIdentifier:myIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:myStyle reuseIdentifier:myIdentifier] autorelease];
}
cell.textLabel.text = @"Title";
cell.detailTextLabel.text = @"Details";
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
#ifndef VIEW_CONTROLLER_MAIN_BODY
@end
#endif
ファイル(子)MyViewController_TextFieldMethods.m:
#import <UIKit/UIKit.h>
#import "MyViewController.h"
#ifndef VIEW_CONTROLLER_MAIN_BODY
@implementation MyViewController
#endif
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
self.myTextField.text = @"Woo hoo!";
return YES;
}
#ifndef VIEW_CONTROLLER_MAIN_BODY
@end
#endif
これは、クラスが大きすぎることを示す良い兆候です。
1つのアプローチ:カテゴリを使用します。多くの場合、宣言はヘッダーにとどまることができます。次に、実装が分割される場合があります。実装しているカテゴリを必ず指定してください。そうすれば、コンパイラはそれを宣言と一致させ、定義を見逃したときに通知します。
大きなクラスを分類する良い方法は、カテゴリのグループ機能です(AppleがUIViewControllerで行うように)。大きなクラスの場合、私は通常、それらのそれぞれの機能に関連するメソッドとプロパティをグループ化し、次にベースヘッダーファイルで分割します
@interface SomeClass : NSObject
@property (strong, nonatomic) BaseProperty *sp;
- (void)someMethodForBase;
@end
@interface SomeClass (FunctionalityOne)
@property (strong, nonatomic) FuncOne *fpOne;
- (void)someMethodForFunctionalityOne;
@end
@interface SomeClass (FunctionalityTwo)
@property (strong, nonatomic) FuncTwo *fpTwo;
- (void)someMethodForFunctionalityTwo;
@end
カテゴリにプロパティを追加できないため(新しいiVarを追加できず、プロパティはカテゴリに合成されません)、ベース拡張の実装で再度宣言します。
@interface SomeClass()
@property (strong, nonatomic) FuncOne *fpOne;
@property (strong, nonatomic) FuncTwo *fpTwo;
@end
@implementation SomeClass
//base class implementation
- (void)someMethodForBase {
}
@end
SomeClass+FunctionalityOne.mでそれぞれの機能を実装する
@implementation SomeClass (FunctionalityOne)
@dynamic fpOne;
//Functionality one implementation
- (void)someMethodForFunctionalityOne {
}
@end
SomeClass+FunctionalityTwo.mでそれぞれの機能を実装する
@implementation SomeClass (FunctionalityTwo)
@dynamic fpTwo;
//Functionality two implementation
- (void)someMethodForFunctionalityTwo {
}
@end
このようにして、大きなクラスの実装を小さなクラスにきれいに整理し、すべてのクラス機能情報を読み取りとインポートのために単一のベースヘッダーにグループ化します。
iOSでカテゴリを使用すると、コードがクリーンに保たれます。
これは古いスレッドであり、Objective Cの神々はこのソリューションを好まないことは知っていますが、ファイルを分割する必要がある場合は、新しい.mファイルを作成してそこにコードを貼り付けてください。次に、@implementationの後に#include"yournewfile.m"を含めたい.mファイルに入れます。コンパイラエラーを回避する秘訣は、ビルドフェーズに進み、コンパイルソースから「yournewfile.m」を削除することです。