6

test.mこれは、動作をテストするために使用しているスタンドアロンファイルです。

コンパイルするには:clang test.m -o test.app -fobjc-arc -ObjC -framework Foundation。Xcodeコマンドラインツールがインストールされていることを確認してください。

#import <Foundation/Foundation.h>

@protocol Protocol

@optional
- (id)objProxyMethod;

@end

@interface ReturnObject: NSObject

@end

@interface Test : NSObject <Protocol>

@end

@interface Proxy : NSObject <Protocol>

- (id)objProxyMethod;

@end

@implementation ReturnObject

- (void)dealloc {
    NSLog(@"ERROR:");
    NSLog(@"I'm getting deallocated!");
    NSLog(@"This shouldn't happen!");
}

- (NSString *)description {
    return @"Blank object!";
}

@end

@implementation Proxy

- (id)objProxyMethod {
    NSLog(@"in [Proxy objProxyMethod]!");
    return [[ReturnObject alloc] init];
}

@end

@implementation Test

- (void)forwardInvocation:(NSInvocation *)invocation {
    NSLog(@"Forwarded invocation!");
    Proxy *proxy = [[Proxy alloc] init];
    [invocation invokeWithTarget: proxy];
    NSUInteger length = [[invocation methodSignature] methodReturnLength];
    if (length == 8) {
        id result;
        [invocation getReturnValue:&result];
    }
}

@end

int main () {
    Test *test = [[Test alloc] init];
    id objResult = [test objProxyMethod];
    NSLog(@"objResult = \"%@\"", objResult);

    return 0;
}

コメントアウトする[invocation getReturnValue:&result];と、返されたオブジェクトは評価されませんdealloc。これがバグなのか、それとも私がどのように機能するのか誤解しているだけなのかはわかりませんNSInvocation

4

3 に答える 3

24

問題はそれresult__strongデフォルトであるということです、それでそれがスコープの外に出るとき、コンパイラはそれのreleaseためにを生成します。ただしgetReturnValue:、返されたオブジェクトの所有権は付与されていないため、メソッドがオブジェクトを解放することはできません。

result:の宣言を変更することでこれを修正できます。

__unsafe_unretained id result;

これにより、コンパイラがスコープ外になったときにreleaseforを生成するのを防ぎます。保持する必要がある場合は、別の変数にコピーできます。resultresult__strong

カテゴリを追加して、NSInvocationこれを処理することもできます。

@interface NSInvocation (ObjectReturnValue)

- (id)objectReturnValue;

@end

@implementation NSInvocation (ObjectReturnValue)

- (id)objectReturnValue {
    __unsafe_unretained id result;
    [self getReturnValue:&result];
    return result;
}

@end

...
    if (length == 8) {
        id result = [invocation objectReturnValue];
    }
...

これをバグとして報告することもできます。コンパイラー、または少なくとも静的アナライザーが、ポインターをstrongポインターからidvoidポインターに変換していることを警告することを期待します。http://bugreport.apple.com

于 2012-08-08T22:40:57.607 に答える
4

これは、ARCがポインタとして記述されたオブジェクトを管理できないためです。直接割り当てのみ。

間違い:

id result;
[invocation getReturnValue:&result];

右:

void *pointer;
[invocation getReturnValue:&pointer];

id result = (__bridge id)pointer; //Correct, ARC will retain pointer after assignment
于 2014-01-29T08:55:42.017 に答える
0
if (length == 8) {
    id result; //this is nil (its also a stack allocated pointer)
    [invocation getReturnValue:&result];  //sets the value to an object
}

...method ends object is deallocated

結果を、スタックに割り当てられていないか、getReturnValueを呼び出さないポインターに設定する必要があります。

APIは、getReturnValueを呼び出したため、保持する(そして場合によっては戻り値を消費する)と想定する場合があります。あなたはしませんでした。getReturnValueを削除すると、戻り値はmainメソッドで正しく返されますか?アップルのドキュメントによると、戻り値は自動的に返されます。

私はそうだと思います。

于 2012-08-08T22:36:58.747 に答える