在 obj-c 中传递块的问题是什么?



Xcode 11.4.1, IOS 13.3.1

背景:为了让一个图形化的、程式化的应用程序启动并运行,我广泛使用了UIAlertController,因为我知道有一天,随着图形设计和插图的最终确定,我会用一个自定义类来替换它。这一天已经(部分(到来,并且某些(但不是全部(UIAlertController实例可以迁移到自定义类。为了简化转换,我构建了自定义类,使其具有相同的占用空间/函数调用,但具有不同的 ENUM 样式。目前,自定义类是从UIAlertController子类化的,并决定是显示股票警报还是自定义警报。最终,当所有自定义警报图形都可用时,我将删除子类,自定义类将独立存在。

这一切都按预期工作,除了我遇到了传递UIAlertAction处理程序的情况,该处理程序因EXC_BAD_ACCESS而崩溃。

首先,我定义块如下:

typedef void(^buttonActionHandler)();

然后我UIAlertAction子类化并添加了一个方便变量来访问处理程序(因为处理程序似乎隐藏在UIALertAction深处,通常无法访问(。

@interface GoodAlertAction : UIAlertAction
{
}
@property buttonActionHandler actionHandler;
@end 

我为actionWithTitle添加了覆盖,这是我看到我不理解的行为的地方。

+(instancetype)actionWithTitle:(NSString *)title style:(GoodAlertActionStyleTypes)style handler:(void (^)(UIAlertAction * _Nonnull))handler
{
if (style == UIAlertActionStyleDefault || style == UIAlertActionStyleCancel || style == UIAlertActionStyleDestructive)
{
//non-migrated alert; use standard UIAlertAction
return (GoodAlertAction *) [UIAlertAction actionWithTitle:title style:(UIAlertActionStyle) style handler:handler]
}
//migrated alert; use custom class
GoodAlertAction action = [GoodAlertAction new];
action.actionHandler = handler;
action.actionHandler();     <--Successfully runs the handler. 
buttonActionHandler testHandler = handler;
testHandler();              <--Crashes with EXC_BAD_ACCESS 

据我所知,action.actionHandlertestHandler的定义相同。那么,为什么前者有效而后者崩溃呢?这是问题的最简单形式,但我实际上发现了它试图在代码后面传递处理程序。

所以问题是:

  1. 我做错了什么/与action.actionHandlertestHandler有什么不同?

我很惊讶这两种方法都有效,因为handler需要一个参数,而buttonActionHandler(可能应该按照惯例称为ButtonActionHandler(则不然。

因此,您应该定义 typedefButtonActionHandler以包含参数:

typedef void(^ButtonActionHandler)(UIAlertAction * _Nonnull action);
typedef UIAlertActionStyle GoodAlertActionStyle;
@interface GoodAlertAction: UIAlertAction
@property (copy) ButtonActionHandler actionHandler;
@end
@implementation GoodAlertAction
+ (instancetype)actionWithTitle:(NSString *)title style:(GoodAlertActionStyle)style handler:(ButtonActionHandler)handler
{
if (style == UIAlertActionStyleDefault || style == UIAlertActionStyleCancel || style == UIAlertActionStyleDestructive)
{
//non-migrated alert; use standard UIAlertAction
return (GoodAlertAction *) [UIAlertAction actionWithTitle:title style:(UIAlertActionStyle) style handler:handler];
}
//migrated alert; use custom class
GoodAlertAction *action = [GoodAlertAction new];
action.actionHandler = handler;
action.actionHandler(action);     // added parameter
ButtonActionHandler testHandler = handler;
testHandler(action);              // added parameter
return action;
}
@end

Rob的评论是提示。我忘了将参数传递给处理程序,这是一个UIAction因此,以下命令正常工作:

testHandler(action);

我只能假设action.actionHandler()没有参数的情况下工作,因为即使处理程序的参数为零,Apple 仍然会选取父action

相关内容

最新更新