1

问题

我有一个__unsafe_unretained id指向已发布对象的指针。到目前为止一切顺利,只要我根本不“使用”指针(特别是,我不通过指针调用任何方法)。但是,当我尝试从方法返回其值时,它会崩溃,即使我已明确指定返回值具有 type __unsafe_unretained id。这是为什么?我想如果我使用__unsafe_unretained,它根本不会调用retain/ release/之类的方法autorelease吗?我想我可以使用__unsafe_unretained id它,就好像它是一个void*(意味着它只做简单的本地作业)?

环境

  • 发展于Xcode 4.4.1
  • 使用iOS SDK 5.1
  • ARCenabled
  • 运行iPhone 4.3 / 5.0 / 5.1 SimulatoriPhone 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 模拟器/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 在返回语句的求值点保留值,然后离开所有局部范围,然后平衡保留,同时确保值跨越调用边界。在最坏的情况下,这可能涉及自动释放,但调用者不能假定该值实际上在自动释放池中。

您的函数GetMember2返回一个id,它是一个可保留的对象类型。因此 ARC 编译器添加retain/autorelease调用以确保在函数返回时返回的对象仍然有效。这会崩溃,因为m_MyMember它没有指向有效的对象。

将返回类型声明为(__unsafe_unretained id)不会改变这种行为,事实上我假设__unsafe_unretained这里被忽略了。

于 2012-10-21T10:42:35.353 回答