2016-11-17 66 views
4

我有結合的間接指針到一個塊中,並返回供以後分配塊成直接指針,這樣的功能:目標C間接指針未能更新直接指針

@interface SomeClass : NSObject 
@property int anInt; 
@end 
@implementation SomeClass 
@end 

typedef void(^CallbackType)(int a); 

- (CallbackType)getCallbackToAssignTo:(SomeClass **)indirectPointer { 
    return ^(int a){ 
    NSLog(@"indirectPointer is: %p", indirectPointer); 
    NSLog(@"*indirectPointer is: %p", *indirectPointer); 
    (*indirectPointer) = [[SomeClass alloc] init]; 
    (*indirectPointer).anInt = a; 
    NSLog(@"After: indirectPointer is: %p", indirectPointer); 
    NSLog(@"After: *indirectPointer is: %p", *indirectPointer); 
    }; 
} 

- (void)iWillNotDoWhatImSupposedTo { 
    SomeClass *directPointer = nil; 
    CallbackType cb = [self getCallbackToAssignTo:(&directPointer)]; 
    NSLog(@"directPointer is pointing to: %p", directPointer); 
    NSLog(@"&directPointer is pointing to: %p", &directPointer); 
    cb(1); 
    NSLog(@"after callback directPointer is: %p", directPointer); 
    NSLog(@"after callback &directPointer is: %p", &directPointer); 
} 

問題是,當所有代碼都編譯並運行時,塊返回時會立即忘記該塊的操作。運行[iWillNotDoWhatImSupposedTo]的打印輸出:

directPointer is pointing to: 0x0 
&directPointer is pointing to: 0x7fff5ce1d060 
--- callback execution starts here 
indirectPointer is pointing to: 0x7fff5ce1d050 
*indirectPointer is pointing to: 0x0 
After assignment: indirectPointer is pointing to: 0x7fff5ce1d050 
After assignment: *indirectPointer is pointing to: 0x61800001e1d0 
--- callback returns here, and the contents of the pointer is lost 
after running callback directPointer is pointing to: 0x0 
after running callback &directPointer is pointing to: 0x7fff5ce1d060 

對我怎樣才能使這個回調的工作任何見解?

+0

不知道如何發生這種情況,但我覺得有趣的是,直接指針所在的地址與indirectPointer的地址不同(分別以60和50結尾) –

+0

假設在您的真實代碼中,重新傳遞一個ivar的地址來設置?否則,你應該讓Block直接返回新的實例。 –

回答

1

我認爲你必須有一個強大的引用來自塊外的新創建的對象。你可以與例如支架類實現這一點:

@inferface Holder: NSObject 
@property (strong) id object; 
@end 
@implementation Holder 
@end 

- (CallbackType)getCallbackToAssignTo:(Holder *)inHolder { 
    return ^(int a){ 
     inHolder.object = [[SomeClass alloc] init]; 
     [inHolder.object setAnInt:a]; 
    }; 
} 

- (void)thisShouldWork { 
    Holder *theHolder = [Holder new]; 
    CallbackType cb = [self getCallbackToAssignTo:theHolder]; 

    cb(1); 
    SomeClass *directPointer = theHolder.object; 
} 
1

這個問題實際上是自動釋放的說法。其實方法簽名:

- (CallbackType)getCallbackToAssignTo:(SomeClass **)indirectPointer; 

實際上等同於:

- (CallbackType)getCallbackToAssignTo:(SomeClass * __autoreleasing *)indirectPointer; 

__autoreleasing用於表示由引用傳遞(ID *)和上回被自動釋放參數。

解決方案是指定__strong作爲生存期限定符。

- (CallbackType)getCallbackToAssignTo:(SomeClass * __strong *)indirectPointer; 

更多,從而可以參照:Double pointer as Objective-C block parameter

4

Idali是正確的,因爲它有一個事實,即參數是一個「指針自動釋放」,你傳遞一個「指針到強」的事情。但重要的是要理解爲什麼這是相關的(因爲在大多數情況下它是不相關的),並且它與引用計數本身無關。

你傳入作爲參數一個「指針強」(directPointerSomeClass * __strong,因此&directPointerSomeClass * __strong *),到一個參數,即「指針自動釋放」。這個怎麼用? ARC通過一種名爲「pass-by-writeback」的技術來處理這個問題。

基本上,它所做的是創建一個「autoreleasing」類型的臨時變量,將強指針賦值給它(如果該值不爲null),然後用指向該臨時變量的指針調用該方法。然後在方法返回後,它將變量的值分配回您的強變量中;事情大致是這樣的:

SomeClass * __autoreleasing temporaryPointer = directPointer; 
CallbackType cb = [self getCallbackToAssignTo:&temporaryPointer]; 
directPointer = temporaryPointer; 

注意,如果-getCallbackToAssignTo:方法只使用參數同步自己的執行中,將有一個SomeClass * __strong *參數和SomeClass * __autoreleasing *參數之間沒有區別,因爲通過逐回寫機制確保在執行該方法期間所做的任何更改都能夠正確反映在您最初嘗試傳遞指針的強變量中。

這裏的問題在於你的方法將指針指針存儲到一個數據結構(塊)中,該數據結構超出了方法的執行(返回塊),這允許一些代碼(塊的主體)稍後使用存儲的指針嘗試修改它指向的指針。但是指針指向的是在方法執行後不再應該使用的臨時變量。所以修改臨時變量並不反映原始變量。

更糟的是,它基本上是未定義的行爲。編譯器認爲臨時變量在傳遞迴寫方案之後不再使用,並且可能允許將該變量的內存重用於其他事情。你所擁有的是一個指向編譯器不希望你仍然使用的變量的懸掛指針。而這個問題並沒有通過將參數類型從「指向自動釋放的指針」改爲「指向強壯的指針」來解決 - 儘管這會消除寫回傳遞,這可能會解決這種直接情況,事實仍然存在該塊可以返回或存儲在一個可以在您指向的變量範圍外使用的位置,這仍然會導致您使用懸掛指針。基本上,當你存儲或有一個塊捕獲一個常規的C指針時,你必須非常小心,因爲塊沒有辦法確保指向的變量的生命週期(不像塊捕獲Objective-C對象指針,在這種情況下,它保留它)。