2

次のような関数を考えてみましょう:

void* getData()
{
    void* data= malloc(32);
    NSData* __autoreleasing dataObject= [NSData dataWithBytesNoCopy: data length: 32 freeWhenDone: YES]
    return data;
}

このコードを実行して値を出力しようとすると (値は初期化されていませんが、テストを行うためだけに) 返されたメモリ領域に出力されますが、例外は発生しません。

しかし、ブレークポイントを設定して生きているオブジェクトのマップを表示しようとすると、データがまだヒープにあるにもかかわらず、生きている NSData オブジェクトがありません。なぜでしょうか?

ARCの前のように、自動解放されたオブジェクトを返す方法を知っています。ARC はすべてを処理しますが、この場合、関数スコープから抜けたため、データの割り当てが解除されます。呼び出し後にそれを有効にして自動解放するにはどうすればよいですか?

4

2 に答える 2

3

これは不可能です。ARC は、アクセスできないオブジェクトの割り当てを自動的に解除するように設計されています。NSData オブジェクトはローカルで返されないため使用できず、ARC は割り当てを解除します。

投稿したコードから判断すると、objective-c オブジェクトでなくても自動的に解放されるデータへのポインターを返したいようです。また、データを生成する関数内で NSData API を使用して、このデータにアクセスできるようにすることもできます。ここにはいくつかのオプションがあります。

  1. NSData オブジェクトを返すようにコードを変更してください。呼び出し元のコードがバッファに直接アクセスしたい場合は、NSData のbytesメソッドを使用できます。ただし、コードが C から呼び出された場合、これは機能しません。

  2. C データの自動解放の試行を停止します。優れた C コードは、いつデータを解放する必要があるかを認識している必要があるため、これは問題になりません。コードを に変更し、データの処理が完了したときにfreeWhenDone: NO呼び出しコードを使用するだけです。free()

  3. この関数を独自のファイルに配置し、そのファイルの ARC を無効にします。これにより、 を手動で呼び出して必要なことを行うことができますがautorelease、残りの関数の参照カウントも手動で処理する必要があります。

  4. 1 と 3 を組み合わせます。ARC を使用して NSData オブジェクトを返す 1 つの関数と、最初に呼び出して自動解放されたオブジェクトを取得し、結果からバッファ ポインタを返す ARC を使用しないラッパー関数を用意します。

于 2013-01-06T20:48:14.250 に答える
2

1つのアプローチは、2番目の関数を追加することです

NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length) 
{ 
    return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES]; 
}  

この関数は、自動解放されたNSDataオブジェクトを返します。

バッファを返す関数からこの関数を呼び出します

void *getAutoreleasedBuffer(NSUInteger length)
{
    void *buffer = malloc(length);
    if (buffer) {
        autoreleaseBufferOfLength(buffer, length);
    }
    return buffer;
}

NSDataから返されたオブジェクトautoreleaseBufferOfLengthは自動解放プールにあるため、自動解放プールが空になるとバッファーが解放されます(でNSData作成されたオブジェクトのautoreleaseBufferOfLength割り当てが解除されるため)。

この関数を使用してこれをデバッグするためにテストしましたmain

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        __unused void * buffer = autoreleasingBuffer(32);

        NSLog(@"%s:exiting @autoreleasepool block", __PRETTY_FUNCTION__);
    }
    NSLog(@"%s:exited @autoreleasepool block", __PRETTY_FUNCTION__);
    return 0;
}

-[NSConcreteData dealloc]アクションでシンボリックブレークポイントを追加し、po @"deallocating NSConcreteData"「評価後に自動的に続行する」をチェックします。これが出力でした

[53030:303] int main(int, const char **):exiting @autoreleasepool block
(NSString *) $0 = 0x0000000100300530 deallocating NSConcreteData
[53030:303] int main(int, const char **):exited @autoreleasepool block

次に、リリースをテストし、テストを少し変更し、で設定された__weakグローバル変数を追加しました。g_dataautoreleaseBufferOfLength

__weak NSData *g_data = nil;
NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length) 
{ 
    return (g_data = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES]); 
}  

メインのログインを変更します。

int main(int argc, const char * argv[])
{
    @autoreleasepool 
    {
        __unused void * buffer = autoreleasingBuffer(32);

        NSLog(@"%s:exiting @autoreleasepool block; g_data = %@", __PRETTY_FUNCTION__, g_data);
    }
    NSLog(@"%s:exited @autoreleasepool block; g_data = %@", __PRETTY_FUNCTION__, g_data);
    return 0;
}

リリース用にビルドして実行すると、次のようになります。

[53934:707] int main(int, const char **):exiting @autoreleasepool block; g_data = 00000000 00000070 00000000 00000070 10000000 00000000 00000000 00000000>
[53934:707] int main(int, const char **):exited @autoreleasepool block; g_data = (null)

これらのテストは、への呼び出しをdataWithBytesNoCopy:length:freeWhenDone:2番目の関数でラップするとautoreleaseBufferOfLength、NSDataオブジェクトを作成し、それを自動解放プールに配置する効果があることを示しています。

:これは良い考えではありません。malloc自動解放プールのドレインによって'dである'dを実現する最良の方法は、@ ughoavgfhwが彼の回答で説明しているように、free(を使用して)ARCなしでビルドされたファイルを追加することです。-fno-objc-arcそれにもかかわらず、このアプローチはある程度興味深いかもしれません。

于 2013-01-06T22:29:41.717 に答える