15

我在寫一個涉及事件處理的API,我希望能夠爲處理程序使用塊。回調通常需要訪問或修改自我。在ARC模式中,Clang警告說,引用自我的塊可能會創建一個保留週期,這似乎是我希望保持一般的有用警告。緊湊地禁用自引用塊的弧保留週期警告

但是,對於我的API的這一部分,回調的生命週期和包含對象在外部維護。我知道當對象被釋放時我可以打破這個循環。

我可以使用#pragma clang diagnostic ignored "-Warc-retain-cycles"關閉每個文件的保留週期警告,但會禁用整個文件的警告。圍繞這個警告,我可以用#pragma clang diagnostic pushpop圍繞這些塊,但是這會使塊變得醜陋。

我也可以通過引用指向self的__weak變量而不是直接引用self來得到警告,但這會使得塊的使用更加不愉快。

我想出了最好的解決方案是這個宏,做各地塊的診斷停用:

#define OBSERVE(OBJ, OBSERVEE, PATH, CODE) \ 
[(OBJ) observeObject:(OBSERVEE) forKeyPath:(PATH) withBlock:^(id obj, NSDictionary *change) { \ 
_Pragma("clang diagnostic push") \ 
_Pragma("clang diagnostic ignored \"-Warc-retain-cycles\"") \ 
do { CODE; } while(0); \ 
_Pragma("clang diagnostic pop") \ 
}]; 

這樣的作品,但它不是API的用戶很發現的,它不允許嵌套觀察員,它與XCode的編輯器交互不佳。有沒有更好的方法來禁用或避免警告?

+10

創建'self'的__weak引用實際上需要一行代碼。我認爲在這種情況下解決問題要比試圖緩解症狀好。如何引用'weakSelf'而不是'self'使得塊的使用更愉快? –

+3

它在一些方面不太愉快。聽衆往往很短,有時只是一個聲明。 __weak聲明使偵聽器的大小加倍。這也意味着你需要限定財產訪問而不是使用推斷自我。我會同意我目前的解決方案可能比只使用__weak更糟糕,但我希望通過這個問題獲得更好的解決方案。 –

+1

你可以改變你的完成塊的原型來接受一個「自我」的論點嗎?現在你傳遞塊的代碼看起來是一樣的(除了接受一個額外的參數),你可以消除警告。 (即讓您的API將相關對象傳遞給您的塊) – nielsbot

回答

7

首先,存在使用#pragma禁用對某些代碼行警告,一種簡單的方式開始:

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "<#A warning to ignore#>" 
<#Code that issues a warning#> 
#pragma clang diagnostic pop 

但我不會用它在這種特殊情況下,因爲它不會解決問題,它只是將它從開發人員那裏隱藏起來。 我寧願選擇Mark提出的解決方案。要創建一個弱引用,你可以做以下的塊之外的一個:

__weak typeof(self) weakSelf = self; // iOS ≥ 5 
__unsafe_unretained typeof(self) unsafeUnretainedSelf = self; // 5 > iOS ≥ 4 
__block typeof(self) blockSelf = self; // ARC disabled 
0

新LLVM是在檢測好/防止這樣的保留週期 等待LLVM運輸與iOS6的或做Alex的方式與創建一個弱變量。

雖然禁用警告是一個壞主意!

1

我認爲禁用警告是目前唯一正確的方法,因爲它說編譯器:不關心這個保留週期,我知道它,我會自己處理觀察者。 引入弱引用是一個代價高昂的解決方案,因爲它帶有運行時CPU和內存開銷。

0

我寫了下面的宏,這我認爲,是相當聰明......

#define CLANG_IGNORE_HELPER0(x) #x 
#define CLANG_IGNORE_HELPER1(x) CLANG_IGNORE_HELPER0(clang diagnostic ignored x) 
#define CLANG_IGNORE_HELPER2(y) CLANG_IGNORE_HELPER1(#y) 

#define CLANG_POP _Pragma("clang diagnostic pop") 
#define CLANG_IGNORE(x)\ 
    _Pragma("clang diagnostic push");\ 
    _Pragma(CLANG_IGNORE_HELPER2(x)) 

它可以讓你做各種有趣的事情(不包括Xcode中你聲嘶力竭地訓話),如..

CLANG_IGNORE(-Warc-retain-cycles) 
[object performBlock:^(id obj){ [obj referToSelfWithoutWarning:self]; }]; 
CLANG_POP 

你可以放入任何警告標誌和鏗鏘會留意你的心血來潮...

CLANG_IGNORE(-Warc-performSelector-leaks); 
return [self performSelector:someIllBegotSelector withObject:arcFauxPas]; 
CLANG_POP 

話又說回來,警告通常是有原因的。黨的poppers。

0

爲了解決創建弱引用的問題,我把它放到一個宏中。它使用預處理器來創建一個新的變種名稱相同,但有一個前綴(「W」,在這種情況下,我避免「弱」,因爲這將是矯枉過正,並用大寫規則惹更多):

#define WEAK_VAR(NAME) __unsafe_unretained typeof(NAME) w##NAME = NAME 

... 
WEAK_VAR(self); 
self.block = ^{ 
    [wself doStuff]; 
}; 

如果,otoh,弱參考是不可取的,不要使用它!我喜歡nielsbot傳遞對象作爲參數的解決方案(當然,如果可能的話)。