2016-10-18 43 views
1

我們的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]; 
} 
+2

就像一個實驗一樣,您可能會嘗試從函數聲明(在'-delegateCall:withObject:'內部)中刪除可變參數,特別是因爲您知道調用(至少在這種情況下)將使用單個參數;可能類似於:void(* func)(__ strong id,SEL,id)=(void(*)(_ strong id,SEL,id))imp; func(delegate,sel,object);'我不知道這會改變行爲,尤其是因爲它在調試版本中工作,但可能值得嘗試查看運行時是否存在確定參數正確排序的問題當符號被剝離時。 – fullofsquirrels

+1

碰撞保留計數'func(委託,sel,(__bridge id)(__ bridge_retained void *)對象);'修復它? –

+0

@fullofsquirrels這個伎倆!我也很喜歡Josh的回答。隨時回答這個問題,我會接受。 –

回答

3

從未嘗過投IMP實例與可變參數的函數,我不能肯定地說怎麼會/應工作(它可能會涉及解析va_list,例如),但因爲你知道你有一個且只有一個參數,我想你應該能夠只是免除了您的可變參數的使用來解決這個特定的問題,當你投你的IMP實例函數指針:

- (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, id) = (void (*)(_strong id, SEL, id))imp; 
      func(delegate, sel, object); 
     } 
    } 
} 

由於你知道這個論點已經是id了,這應該是一個完全安全的替代品。

至於爲什麼您的原始實現工作在調試版本但不是在發佈版本中,我只能猜測;它可能與這樣的事實有關:發佈版本通常會在鏈接期間剝離所有符號,並且運行時可能能夠利用符號(如果存在),以便在調用時猜測正確的參數排序?也許編譯器在發佈配置時使用錯誤的調用約定來生成調用函數,並聲明具有固定參數佔用空間,但使用可變參數調用?如果有人對調試/發佈問題有更明確的答案,我會對進一步的信息感興趣。

請參閱有關調用約定here的討論,以獲取使用reinterpret_cast的可能備選方案,如果實際上您的問題是由於調用約定不匹配造成的。

相關問題