为什么在将objective-c处理程序强制转换为void*类型之前需要复制它?

为什么在将objective-c处理程序强制转换为void*类型之前需要复制它?,objective-c,objective-c-blocks,Objective C,Objective C Blocks,我想模拟只存在于10.9上的NSAlert成员函数 - (void)beginSheetModalForWindow:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse returnCode))handler NS_AVAILABLE_MAC(10_9); 代码如下: -(void)compatibleBeginSheetModalForWindow: (NSWindow *)sheetWindow

我想模拟只存在于
10.9
上的
NSAlert
成员函数

- (void)beginSheetModalForWindow:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse returnCode))handler NS_AVAILABLE_MAC(10_9);
代码如下:

-(void)compatibleBeginSheetModalForWindow: (NSWindow *)sheetWindow
                        completionHandler: (void (^)(NSInteger returnCode))handler
{
    void *handlerValue = (__bridge void*) [handler copy];
    [self beginSheetModalForWindow: sheetWindow
                     modalDelegate: self
                    didEndSelector: @selector(blockBasedAlertDidEnd:returnCode:contextInfo:)
                       contextInfo: handlerValue];
}

-(void)blockBasedAlertDidEnd: (NSAlert *)alert
                  returnCode: (NSInteger)returnCode
                 contextInfo: (void *)contextInfo
{
    void(^handler)(NSInteger) = (__bridge typeof(handler)) contextInfo;
    handler(returnCode);
    [handler release];
}
我需要先复制处理程序,然后再将其强制转换为
void*
,否则,如果我将其更改为以下行,它将崩溃:

void *handlerValue = (__bridge void*) handler;
通过检查
blockbasedalertdiden:returnCode:contextInfo:
中的
处理程序
,该值不正确

即使我在转换到
void*
之前调用
[handler retain]
,它也不起作用。
这让我很困惑,为什么我需要复制它?

这是因为块是在堆栈上创建的,而不是在堆上创建的。因此,当堆栈指针在堆栈帧中的内存上移动时,返回堆栈帧并丢失块。这就是为什么您总是应该复制块而不是保留它们。当您在块上运行copy时,新的副本将在堆上分配,因此不会被删除。

Peter Segerblom介绍了它的基本原理。在当前的实现中,有3种块。不捕获局部变量的块是全局块;只有一个实例具有静态生存期。捕获局部变量的块开始时是堆栈上的对象;复制它将在堆上返回一个块。复制堆或全局块只会返回相同的实例

基本上,传入函数的块可以是堆栈块,也可以不是堆栈块。堆栈块仅对当前函数调用有效。由于它可能是堆栈块,如果要将其存储在当前函数调用之外的某个位置,则必须使用它的副本。这是采用块参数的函数的约定


现在,如果您的函数对这个块所做的只是将它传递给另一个函数,那么您需要传递一个副本吗?如果此函数参数为块类型,则为否,因为如果此函数需要保留块供以后使用,则它负责复制(根据上述合同)。所以你不必担心。但是,如果将其传递给非块类型的函数(例如,
-[NSMutableArray addObject:
),则该函数不知道可能复制它(它甚至不知道它是块)。在这种情况下,如果该函数保留对象供以后使用,则必须传递副本。您要传递到此处的函数就是这种情况。

但block不是Objective-C NSObject子类对象吗?我认为它的复制成员函数是从NSObject继承的。块是对象,但我认为它们不是从NSObject继承的。我不知道它们是如何实现的,但我猜它是一个C函数指针。据我所知,块是Objective-c中唯一在堆栈上分配的对象。@PeterSegerblom:blocks(在Apple实现中)是NSObject的实例。为什么要在MRC中使用
\u bridge
强制转换?@newacct,
\u bridge
不调用
retain
,对吗?你的意思是我应该删除
\uu桥
,为什么我不应该使用它,我假设它只是执行类型case;你应该得到一个警告。它们仅在ARC中有意义,用于指示是否应将非对象指针视为“保留”。但是,如果您将其移动到ARC,您所使用的桥强制转换将不正确,因为您必须在对象处于
void*
时保留该对象,因此从对象指针向
void*
强制转换时,您需要使用
\u bridge\u retained
,和
\uuuu bridge\u transfer
将其从
void*
中抛出以消耗保留计数。解释得很好。那么,有没有关于块行为的文档或标准,就像lambda如何定义C++标准?我读过苹果的文档,但即使是苹果也没有提到全局块/堆栈块/堆块。@ZijingWu:其中一些是实现细节,但基本上你可以假设所有块(可能)都是从堆栈开始的,因此必须复制以在定义的范围之外使用。Blocks语言规范并没有具体说明这一点()。但是ARC规范的背景部分清楚地说明了这一点()。