2

カスタム クラスでNSFastEnumerationcountByEnumeratingWithState:objects:count:プロトコルのメソッドを実装しようとしています。

これまでのところ、オブジェクトを正しく反復処理していますが、返されるオブジェクトは Objective-C オブジェクトではなく、コアとなる基盤の同等物です。

state->itemsPtr を設定するコードの一部を次に示します。

MyCustomCollection.m

- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
                                   objects: (id __unsafe_unretained *)buffer
                                     count: (NSUInteger)bufferSize {

    // ... skip details ...

    NSLog(@"Object inside method: %@", someObject);
    state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;      

    // ... skip details ...
}

次に、このように「for..in」ループを別の場所で呼び出します

SomeOtherClass.m

MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:@"foo"];
for (id object in myCustomCollection) {
    NSLog(@"Object in loop: %@", object);
}

コンソール出力は次のとおりです。

Object inside method: foo
Object in loop: __NSCFConstantString

ご覧のとおり、NSFastEnumeration プロトコル メソッド内では、オブジェクトは正常に出力されますが、キャストされるとすぐにid __unsafe_unretained *、元の Objective-C 対応クラスが失われます。

正直なところ(__unsafe_unretained id *)(__bridge void *)、この場合のキャスティングがどのように機能するかはよくわかりません。は(__unsafe_unretained id *)、適切なタイプの itemsPtr のニーズに一致するようにキャストされているようです。は(__bridge void *)、obj-c ワールドを CF ワールドにブリッジするために使用される __bridge を使用して、タイプ void のポインターにキャストするようです。llvm docsに従って、次の場合__bridge:

所有権の譲渡はなく、ARC は保持操作を挿入しません。

あれは正しいですか?

私の理解では、__NSCFConstantString は NSString に相当するコア基盤にすぎません。また、ARC では、Objective-C オブジェクトから同等の CoreFoundation オブジェクトにブリッジする必要があることも理解しています。これは、ARC が後者のメモリを管理する方法を知らないためです。

「for..in」ループ内のオブジェクトが元のタイプになるようにするにはどうすればよいですか?

また、この場合、コレクションに NSStrings を追加していますが、理論的にはすべてのオブジェクトをサポートする必要があることに注意してください。

アップデート

ロブの答えは正しい軌道に乗っていますが、その理論をテストするために、for ループを次のように変更しました。

for (id object in myCustomCollection) {
    NSString *stringObject = (NSString *)object;
    NSLog(@"String %@ length: %d", stringObject, [stringObject length]);
}

理論的には、オブジェクトは同等であるため機能するはずですが、次のエラーでクラッシュします。

+[__NSCFConstantString length]: unrecognized selector sent to class

ループで返されるオブジェクトは、forインスタンスではなくクラスのように見えます。ここで何か他のことが間違っているかもしれません...これについて何か考えはありますか?

更新 2: ソリューション

次のように簡単です: (CodaFi のおかげで

state->itemsPtr = &someObject;
4

2 に答える 2

3

あなたは間違ってキャストしていsomeObjectます。あなたが意味したことは次のとおりです。

state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;

(それらのひどいキャストも取り除きましょう)

state->itemsPtr = &someObject;

address-of がないと、変数は最初のポインターに押し込まれ、ループで逆参照されます。逆参照されると (基本的には)、オブジェクトではなく*id、基になる objc_object のクラス ポインターが取得されます。isaこれが、デバッガーが列挙子呼び出し内に文字列の値を出力し、ループ内にオブジェクトのクラスを出力し、結果のポインターにメッセージを送信すると例外がスローされる理由です。

于 2013-06-15T06:19:03.687 に答える
1

あなたのコードはそのままで問題ありません。デバッグ出力は、実装の詳細を明らかにしています。

NSStringと無料でブリッジされていCFStringます。NSStringこれは、ポインターを他の型にキャストするだけで、 anyを としてCFString、またはその逆に 扱うことができることを意味します。

実際、内部では、コンパイル時の定数文字列は type のインスタンスであり__NSCFConstantString、これが表示されているものです。

@"hello"ソース コードを挿入すると、コンパイラはそれを として扱い、 のインスタンスにNSString *コンパイルします__NSCFConstantString

CFSTR("hello")ソース コードを挿入すると、コンパイラはそれを として扱い、 のインスタンスにCFStringRefコンパイルします__NSCFConstantString

ソース コードで異なる構文を使用してこれらのオブジェクトを作成したとしても、実行時にメモリ内のこれらのオブジェクトに違いはありません。

于 2013-06-15T06:07:54.103 に答える