1

問題

__unsafe_unretained idすでにリリースされているオブジェクトを指すポインタがあります。これまでのところ、ポインターをまったく「使用」しない限り(特に、ポインターを介してメソッドを呼び出さない限り)、問題はありません。ただし、メソッドからその値を返そうとすると、戻り値の型が。であることを明示的に指定した場合でも、クラッシュ__unsafe_unretained idします。何故ですか?を使用すると、 //__unsafe_unretainedのようなメソッドはまったく呼び出されないと思いましたか?私はそれが(単純なネイティブの割り当てのみを行うことを意味する)であるかのようにほとんど使用できると思いましたか?retainreleaseautorelease__unsafe_unretained idvoid*

環境

  • で開発Xcode 4.4.1
  • 使用するiOS SDK 5.1
  • ARCenabled
  • iPhone 4.3 / 5.0 / 5.1 Simulatorまたはで実行iPhone 4.3 Device
  • 両方でクラッシュしDebugReleaseビルドします

ソースコード

// Declare my class with 1 member.
@interface MyClass : NSObject
{
    __unsafe_unretained id      m_MyMember;
}
@end

// **************************************************************************************************** //

// Implement my class.
@implementation MyClass

// Setter
-(void)SetMember:(__unsafe_unretained id)member
{
    m_MyMember = member;
}

// Getter: by passing parameter by reference
-(void)GetMember1:(__unsafe_unretained id*)member
{
    *member = m_MyMember;   // No problem.
}

// Getter: by return value
-(__unsafe_unretained id)GetMember2
{
    return m_MyMember;  // Crashed in here!
}

@end

// **************************************************************************************************** //

//! Application entry point.
int main(int argc, char *argv[])
{
    @autoreleasepool
    {       
        {
            // Create an object that dies immediately. deadObj is a dangling pointer.
            __unsafe_unretained id deadObj = [[NSMutableString alloc] initWithFormat:@"%d", 12];

            // Create my object.
            MyClass* myObject = [[MyClass alloc] init];

            // Assign my member.
            [myObject SetMember:deadObj];

            // Get back my member: by passing parameter by reference
            __unsafe_unretained id unsafePointer1;
            [myObject GetMember1:&unsafePointer1];  // No problem.

            // Get back my member: by return value
            __unsafe_unretained id unsafePointer2;
            unsafePointer2 = [myObject GetMember2]; // Crashed in here!

            int BreakpointHere = 0;
        }
    }
}

コールスタック(iPhone 4.3 Simulator / iOS 4.3デバイス):

#0  0x011db09b in objc_msgSend ()
#1  0x00106712 in __arclite_objc_retainAutoreleaseReturnValue at /SourceCache/arclite_host/arclite-29.1/source/arclite.m:259
#2  0x00001fec in -[MyClass GetMember2] at /Users/user/SourceCode/main.m:28
#3  0x00002147 in main at /Users/user/SourceCode/main.m:56

コールスタック(iPhone 5.0 / 5.1シミュレーター):

#0  0x014f6d25 in objc_retain ()
#1  0x014f7fe3 in objc_retainAutoreleaseReturnValue ()
#2  0x00001fec in -[MyClass GetMember2] at /Users/user/SourceCode/main.m:28
#3  0x00002147 in main at /Users/user/SourceCode/main.m:56
4

1 に答える 1

1

動作は以下の情報で説明できると思います3.2.3。自動参照カウントのドキュメントで保持されていない戻り値

保持可能なオブジェクトタイプを返すが、保持された値を返さないメソッドまたは関数は、オブジェクトが戻り境界を越えて有効であることを確認する必要があります。

このような関数またはメソッドから戻る場合、ARCはreturnステートメントの評価の時点で値を保持し、次にすべてのローカルスコープを離れ、値が呼び出し境界を越えて存続することを保証しながら、保持のバランスを取ります。最悪の場合、これには自動解放が含まれる可能性がありますが、呼び出し元は、値が実際に自動解放プールにあると想定してはなりません。

関数は、保持可能なオブジェクト型であるをGetMember2返します。idしたがって、ARCコンパイラはretain/autorelease呼び出しを追加して、関数が戻ったときに返されたオブジェクトが引き続き有効であることを確認します。m_MyMemberが有効なオブジェクトを指していないため、これはクラッシュします。

リターンタイプをとして宣言しても、(__unsafe_unretained id)この動作は変わりません。実際、__unsafe_unretainedここでは無視されていると思います。

于 2012-10-21T10:42:35.353 に答える