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.actionHandler
和testHandler
的定义相同。那么,为什么前者有效而后者崩溃呢?这是问题的最简单形式,但我实际上发现了它试图在代码后面传递处理程序。
所以问题是:
- 我做错了什么/与
action.actionHandler
testHandler
有什么不同?
我很惊讶这两种方法都有效,因为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
。