【发布时间】:2017-02-28 09:38:50
【问题描述】:
我们的 Swift 应用程序需要一些较低级别的 C/Objective-C 代码,因此我们添加了一个动态库来简化与应用程序的集成。
该库有一个单一的、共享的控制器实例,但回调的样式不适用于闭包,因此我们采用了协议。然而,由于多个类需要使用这个控制器,它需要有多个委托。因此,每个类都将自己注册为委托,当调用协议方法时,它会遍历每个委托,获取选择器的 IMP,然后调用它。
在调试构建时这工作正常,直到我们使用 Release 配置,我们才注意到这些函数的参数在协议方法的实现中为 nil,即使它们在调用时不是 nil。
这就是我们的协议方法的调用方式:
- (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),则不会发生问题:
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);我不知道这会改变行为,特别是因为它在调试版本中工作,但也许值得一试,看看运行时是否存在问题,当符号是时确定参数的正确顺序剥离。 -
增加保留计数
func(delegate, sel, (__bridge id)(__bridge_retained void *)object);可以解决问题吗? -
@fullofsquirrels 成功了!我也喜欢乔希回答的安全性。随意回答问题,我会接受。
-
我的建议实际上可能会导致泄漏! :) 我只是想检查内存管理是否存在问题。
-
fullofsquirrels 的想法应该正确修复内存管理。
标签: ios objective-c swift