2012-10-18 59 views
1

問題返回__unsafe_unretained指針在目標C釋放的對象導致崩潰

我有一個__unsafe_unretained id指針指向一個已經釋放的對象。到目前爲止,只要我根本不「使用」指針(特別是,我沒有通過指針調用任何方法),那麼這麼好。但是,當我嘗試從方法中返回其值時,即使我明確指定返回值的類型爲__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 

回答

1

我認爲行爲可以用下面的信息3.2.3. Unretained return values自動引用計數文檔來解釋:

它返回一個可保持對象類型但 不返回保留值必須在方法或功能確保對象在返回邊界上仍然有效 。

當從這樣的函數或方法返回,ARC在return語句的評價點的值仍保持 ,然後離開所有 本地範圍,然後平衡了保留,同時確保 值生命跨越呼叫邊界。在最壞的情況下,這可能是 涉及自動釋放,但呼叫者不能認爲該值實際上在自動釋放池中是 。

您的功能GetMember2返回一個id,它是一個可保留的對象類型。因此,ARC編譯器添加了retain/autorelease調用,以確保函數返回時返回的對象仍然有效。這會崩潰,因爲m_MyMember不指向有效的對象。

聲明返回類型爲(__unsafe_unretained id)不會改變這種行爲,實際上我假設__unsafe_unretained在這裏被忽略。

+0

獲得的經驗教訓: 1.始終__nil__指針死前的__unsafe_unretained指針。 2.使用'__weak'代替(需要iOS 5運行時)。 – Pang