在 obj-c 中传递块的问题是什么?
What is this problem passing blocks in obj-c?
Xcode11.4.1,IOS13.3.1
背景:为了获得一个图形化的、程式化的应用程序并 运行ning,我广泛地使用了 UIAlertController
,我知道有一天,随着图形设计和艺术作品的完成,我会用自定义 class。那一天已经(部分)到来,UIAlertController
的一些(但不是全部)实例可以迁移到自定义 class。为了简化转换,我构建了自定义 class 以具有相同的 footprint/function 调用但具有不同样式的 ENUM。目前,自定义 class 是 UIAlertController
的子class,并决定是显示股票警报还是自定义警报。最终,当所有自定义警报图形都可用时,我将删除子 classing,自定义 class 将独立。
除了我 运行 遇到传递 UIAlertAction
处理程序并因 EXC_BAD_ACCESS
.
而崩溃的情况外,这一切都按预期工作。
首先,我将块定义如下:
typedef void(^buttonActionHandler)();
然后我子classed 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
的定义相同。那么,为什么前者工作而后者崩溃呢?这是问题的最简单形式,但我实际上发现它试图在代码的后面传递处理程序。
所以问题是:
- 我在 wrong/different 和
testHandler
对比 action.actionHandler
做什么?
Rob 的评论是提示。我忘记将参数传递给处理程序,它是一个 UIAction
因此以下命令正常工作:
testHandler(action);
我只能假设 action.actionHandler()
在没有参数的情况下工作,因为即使处理程序的参数为 nil,Apple 仍然会选择父 action
。
考虑到 handler
带有参数,而 buttonActionHandler
(按照惯例可能应该称为 ButtonActionHandler
)没有参数,我感到很惊讶。
因此,您应该定义 ButtonActionHandler
typedef 以包含参数:
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
Xcode11.4.1,IOS13.3.1
背景:为了获得一个图形化的、程式化的应用程序并 运行ning,我广泛地使用了 UIAlertController
,我知道有一天,随着图形设计和艺术作品的完成,我会用自定义 class。那一天已经(部分)到来,UIAlertController
的一些(但不是全部)实例可以迁移到自定义 class。为了简化转换,我构建了自定义 class 以具有相同的 footprint/function 调用但具有不同样式的 ENUM。目前,自定义 class 是 UIAlertController
的子class,并决定是显示股票警报还是自定义警报。最终,当所有自定义警报图形都可用时,我将删除子 classing,自定义 class 将独立。
除了我 运行 遇到传递 UIAlertAction
处理程序并因 EXC_BAD_ACCESS
.
首先,我将块定义如下:
typedef void(^buttonActionHandler)();
然后我子classed 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
的定义相同。那么,为什么前者工作而后者崩溃呢?这是问题的最简单形式,但我实际上发现它试图在代码的后面传递处理程序。
所以问题是:
- 我在 wrong/different 和
testHandler
对比action.actionHandler
做什么?
Rob 的评论是提示。我忘记将参数传递给处理程序,它是一个 UIAction
因此以下命令正常工作:
testHandler(action);
我只能假设 action.actionHandler()
在没有参数的情况下工作,因为即使处理程序的参数为 nil,Apple 仍然会选择父 action
。
考虑到 handler
带有参数,而 buttonActionHandler
(按照惯例可能应该称为 ButtonActionHandler
)没有参数,我感到很惊讶。
因此,您应该定义 ButtonActionHandler
typedef 以包含参数:
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