2

XcodeのTabBarControllerテンプレートでいくつかのことを試しているときに、この「問題」に遭遇しました(実際には問題ではありません。それが可能であることに驚いただけです)。ストーリーボードなしでテンプレートを使用する場合、基本的な設定は次のようになります。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1 = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];

self.tabBarController.viewControllers = @[viewController1, viewController2];

self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}

tabBarControllerのviewControllersプロパティはNSArrayです。したがって[self.tabBarController objectAtIndex:0]、を返しますid

そのため、FirstViewControllerクラスで宣言したメソッドを呼び出したい場合は、次のようにする必要があると常に考えていました。

FirstViewController *firstVC = (FirstViewController *)[self.tabBarController objectAtIndex:0];
[firstVC someMethod];

しかし、それが不必要であることが判明したので、コンパイラーは私に次のこともさせてくれます-宣言するヘッダーファイルをインポートするsomeMethod限り(もちろん、それは必ずしも読みやすさを向上させるわけではありませんが、とにかく):

[[self.tabBarController objectAtIndex:0] someMethod];

私はこれをまったく期待していませんでした。idしたがって、コンパイラは、現在のクラスのスコープ内の任意のクラスでそのメソッドが宣言されている限り、任意のメソッドの呼び出しを許可すると想定しています(つまり、ヘッダーファイルは現在のクラスにインポートされます)。クラス宣言someMethodがインポートされない場合、コンパイラはエラーをスローします(ただし、ARCの使用中にこれをテストしたことを追加する必要があります。コンパイラが「インポートされていない」メソッドの呼び出しについて文句を言わない可能性がありますid。 ARCを使用しない)..。

その仮定は正しいですか?また、可能であれば、idタイプに関する詳細情報やリファレンスを提供していただけますか?

または、コンパイラはARCの前に(インポートされているかどうかに関係なく)任意のメソッドを呼び出すことを許可しましたidか?インポートされていないメソッドに対する苦情は、ARCの結果にすぎませんか?

ありがとう。

4

2 に答える 2

1

オブジェクト(タイプidは通常、から継承するオブジェクトのインスタンスですNSObject)にはメソッドがあります。オブジェクトはメソッドシグネチャに応答するか、応答しません。

XCodeは、これが型チェックを介してそのメソッドに応答しないことについて警告をスローする場合がありますが、コードは引き続き実行されます。また、それをサポートしていないオブジェクトでメソッドを実行すると、実行時例外が発生します。他のタイプの完全に互換性のないオブジェクトと同様に、任意のオブジェクトをキャストし、実行時にそのオブジェクトのメソッドを呼び出すことができます。

// This should work at runtime, but generate warning when compiled
// Don't do this, obviously, but it should "work"
NSNumber *array = [NSArray arrayWithObject:@"foobar"];
NSLog([array objectAtIndex:0]); // "foobar"

タイプチェックインスタンスとそれらで呼び出されるメソッドは、コンパイル時にプログラマーにとってはメリットがありますが、実行時にコンパイルされたアプリケーションにとってはそれほどメリットはありません。

だからあなたの質問に答えるために:

コンパイラは、オブジェクトに送信できるメッセージをどのように判断しますか?

実行時にオブジェクトに問い合わせます。

[myObj myMethod:123];

ObjCランタイムで次の擬似コードのようなものをトリガーします。

if myObj responds to the message with signature "myMethod:"?
  send myObj the "myMethod:" message with arguments [123]
else
  throw an exception

変数はオブジェクトへの単なるポインタであるため、変数によって宣言された型は、実行時に実際にはまったく重要ではありません。そのオブジェクトが特定のメソッドに応答するかどうかの把握は、コンパイル時に行われません。

于 2012-10-25T21:16:54.720 に答える
1

または、コンパイラはARCの前にid(インポートされているかどうかに関係なく)のメソッドを呼び出すことを許可しましたか?インポートされていないメソッドに対する苦情は、ARCの結果にすぎませんか?

正解です。ARCがなければ、それは警告でした。ARCの場合、これはエラーです(ARCがここで間違った推測をすると、深刻な問題が発生する可能性があるため)。

場合によっては、この動作により、ARCの有無にかかわらず非常に微妙なバグが発生する可能性があります。セレクターに一致するメソッドシグネチャが複数ある場合、コンパイラーは間違った戻り型を選択する可能性があり、これにより非常に驚くべき実行時の動作が発生する可能性があります。Matt Gallagherは、 「Objective-Cの弱いタイピングの大きな弱点」でこれを非常によく説明しています。私は彼が説明しているのと同じバグに遭遇しました。それはそれほど頻繁には発生しませんが、ObjC開発者が知っておくべきことです。

于 2012-10-25T21:45:56.940 に答える