我們的Swift應用程序需要一些較低級別的C/Objective-C代碼,因此我們添加了動態庫以使與應用程序的集成更容易。當在發佈配置中使用IMP調用時,方法參數爲零
該庫有一個控制器的單個共享實例,但回調的樣式對於閉包不起作用,所以我們使用了一個協議。然而,由於多個類需要使用這個控制器,它需要有多個代表。所以每個類都將自己註冊爲一個委託,並且在調用協議方法時,它會迭代每個委託,獲取選擇器的IMP並調用它。
在調試版本上工作正常,直到我們使用Release配置,我們注意到這些函數的參數在協議方法的實現中是零,即使它們在調用時不是零。
這是怎麼了我們的協議的方法稱爲:
- (void) delegateCall:(SEL)sel withObject:(id)object {
for (id delegate in self.delegates) {
if ([delegate respondsToSelector:sel]) {
IMP imp = [delegate methodForSelector:sel];
void (*func)(__strong id,SEL,...) = (void (*)(__strong id, SEL, ...))imp;
func(delegate, sel, object);
}
}
}
讓我們用例子協議方法:- (void) blah:(NSNumber * _Null_unspecified)aNumber;
如果我們調用[self delegateCall:@selector(blah:) withObject:@32];
,對象將是零在blah
實施:
func blah(_ aNumber: NSNumber) {
if aNumber == nil {
print("The number is nil somehow?!?!?!") // <-- Release
} else {
print("The number is: \(aNumber.intValue)") // <-- Debug, prints 32
}
}
如果我們在代表中使用調用代碼中的方法(而不是使用IMP),那麼問題就沒有了ppen:
for (id delegate in self.delegates) {
[delegate blah:@32];
}
就像一個實驗一樣,您可能會嘗試從函數聲明(在'-delegateCall:withObject:'內部)中刪除可變參數,特別是因爲您知道調用(至少在這種情況下)將使用單個參數;可能類似於:void(* func)(__ strong id,SEL,id)=(void(*)(_ strong id,SEL,id))imp; func(delegate,sel,object);'我不知道這會改變行爲,尤其是因爲它在調試版本中工作,但可能值得嘗試查看運行時是否存在確定參數正確排序的問題當符號被剝離時。 – fullofsquirrels
碰撞保留計數'func(委託,sel,(__bridge id)(__ bridge_retained void *)對象);'修復它? –
@fullofsquirrels這個伎倆!我也很喜歡Josh的回答。隨時回答這個問題,我會接受。 –