2012-05-24 58 views
0

我已經創建了一個類NSURLConnection的包裝類,並且類的用戶傳遞了在連接的各個階段被調用的塊。包裝類如下所示,爲了簡潔起見簡化:你如何讓一個塊成爲一個班級的成員?

typedef void (^ConnectionFinishedWithErrorBlock)(NSError* error); 

@interface MYHTTPConnection : NSObject <NSURLConnectionDelegate> 
@property (copy, nonatomic)  ConnectionFinishedWithErrorBlock theErrorBlock; 
@property (strong, nonatomic) NSURLConnection *connection; 

- (void) establishConnectionForURL:(NSURL*) url 
     andConnectionFinishedWithErrorBlock: (ConnectionFinishedWithErrorBlock) errorBlock; 
@end 

到目前爲止,我已經使用它與聯聲明即

[self.httpConnection establishConnectionForURL: url 
      andConnectionFinishedWithErrorBlock:^(NSError* error) 
      { 
       ... 
      }]; 

塊被但現在我有一個情況我有一個非常長的錯誤處理塊,並將其所有內聯代碼放在一起將會變得混亂(因爲API需要其他塊未在此處顯示)。

我知道我可以做這樣的事情:

void (^httpConnectionFinishedWithError)(NSError*) = 
^(NSError* error) 
{ 
} 

然後傳遞到httpConnectionFinishedWithError establishConnectionForURL。但httpConnectionFinishedWithError包含對自我的調用。當聲明內聯塊時,對自我的調用是正確的,但在完成上述操作時不會發生這種情況,因爲顯然這會產生編譯錯誤。

所以我想知道是否/如何塊可以作爲調用類的塊屬性? (我之前已經將塊用作類的屬性,正如在上面的MYHTTPConnection類中所做的那樣,但在這種情況下,我無法獲得將傳遞給establishConnectionForURL的塊添加爲屬性的語法權限呼叫類)。

或者,httpConnectionFinishedWithError可以調用另一個函數,但在這種情況下,我不得不將自己作爲參數傳遞,在這種情況下如何獲得塊內的自我?

還有其他更優雅的解決方案嗎?

非常感謝

編輯: 更新,以顯示實例編譯錯誤的

void (^httpConnectionFinishedWithError)(NSError*) = 
^(NSError* error) 
{ 
    self.boolProperty = NO; // here 
} 
+0

«當塊被聲明爲內聯時,對自我的調用是正確的,但是當完成上述操作時不會發生這種情況,因爲顯然這會產生編譯錯誤。»這根本不清楚。你在做什麼工作?任何ObjC方法中都有一個有效的自我指針。如果你在類方法中,'self'指向類而不是實例 - 是問題所在? –

+0

是的,它需要訪問實例屬性和方法。 – Gruntcakes

+0

所以_你在做什麼? –

回答

2

如果您使用ARC,您可以定義一個弱變量引用自:

__weak ClassOfSelf *_self = self;

並使用該塊。如果你沒有使用ARC,我相信你想使用__block而不是弱。在這兩種情況下,該塊不會保留自己,因此您可以避免保留週期。

我知道的存儲/在課堂上使用塊,最好的辦法是先typedef定義它:

typedef void (^HttpConnectionFinishedWithError)(NSError*);

然後創建了塊存儲的實例變量:

HttpConnectionFinishedWithError httpCompletionBlock;

在你的init中創建塊並將其分配給實例變量。不過,別忘了先複製塊,使得它移動到堆(塊堆棧,這意味着他們他們在創建範圍後消失在創建消失):

- (id) init{ 
    if (self = [super init]){ 
    __weak id _self = self; //or use class of self instead of id 
    httpCompletionBlock = [^(NSError *err){ 
     .....code here, use _self instead of self 
    } copy]; 
    } 
    return self; 
} 

(使用ARC例子)

現在httpCompletionBlock可以像課堂上一樣正常使用。只要在塊代碼中使用_self而不是self,則不會直接引用塊中的任何實例變量,因此不會創建保留週期。請記住引用_iVarself->_iVar相同。所以該塊將捕獲self。而是使用:_self->_iVar或創建一個property/get-method來訪問實例變量。

如果您使用的是ARC,但是定位到iOS 4,則不能使用__weak,因爲它不可用,並且您不能使用__block,因爲它不會阻止該塊將該變量保留在ARC下。相反,您可以使用__unsafe_unretained。只要記住__unsafe_unretained不會解析爲nil如果該變量引用的對象是dealloc'd ...因此它是不安全的。你需要小心你如何使用它。例如,如果你傳遞另一個對象這個塊並且self得到dealloc'd。任何對_self的引用都會導致錯誤。解決此問題的一種方法是創建一個內聯「中間」塊,只需要塊就可以保留自己。例如,而不是直接傳遞塊,做這樣的事情:

self.httpConnection establishConnectionForURL: url andConnectionFinishedWithErrorBlock:^(NSError* error) 
      { 
       httpCompletionBlock(error); 
      }]; 

在上述情況下,httpcompletionBlock是實例變量,所以self獲取的通過在局部範圍內的取景拍攝。現在你放心,self不會消失,直到執行httpCompletionBlock之後。您仍然避免了一個保留循環,因爲只有內聯塊保留self,而不是保存的塊。

+0

謝謝,是的,我使用ARC,但也針對OS4。是否有任何向後兼容性問題需要考慮__weak? – Gruntcakes

+0

是的,有,我不相信__weak在iOS 4中可用。我會更新我的答案。 –

+0

好吧,我已經更新了使用ARC針對iOS 4平臺的答案。 –

0

使用性能的替代方法是從方法返回塊,像這樣:

-(ConnectionFinishedWithErrorBlock)connectionFinishedWithErrorBlock { 
    return ^(NSError* error) { 
     self.boolProperty = NO; 
    }; 
} 

[self.httpConnection establishConnectionForURL:url 
      andConnectionFinishedWithErrorBlock:[self connectionFinishedWithErrorBlock]]; 

毫無保留週期,因爲你的類沒有握住塊的一個實例。在回調後httpConnection不會處理該塊,並且物體會泄漏。如果您不擁有任何httpConnection,請確保您瞭解它的語義,並在Instruments中測試以確保它不會泄漏連接完成的塊。

+0

這看起來很整齊。 httpConnection是發佈中的MYHTTPConnection類 – Gruntcakes

+0

因此,您應該確保MyHTTPConnection在使用後處理該塊:if(self.completionBlock){self.completionBlock(error); self.completionBlock = nil;}' –

相關問題