从块、NSAlert、OS X 管理 C 字符串



我写了一段测试代码"AlertTest",以确保我以正确的方式实现NSAlert对象。它由一个触发 [doSomething: cstr] 打印 C 字符串的按钮和一个发布警报的方法组成,该方法将相同的字符串传递给相同的方法,但来自完成处理程序。这是调试器控制台打印输出:

-从 [按钮按下]

2015-01-09 18:28:09.832 AlertTest[1260:40881] Application must quit
Application must quit

-from [mPostError:cstr]

2015-01-09 18:28:12.276 AlertTest[1260:40881] @Èø_ˇ
@351277_377

我对必须做什么才能从完成处理程序中正确传递字符串感到有点困惑。未报告编译器错误。我一定错过了一些非常明显的东西。提前谢谢。代码如下:

//___________________________________________________________
- (IBAction)buttonPressed:(NSButton *)sender {
char cstr[256];
strcpy(cstr, "Application must quit");
[self mDoSomething:cstr];
[self mPostError:cstr];
}
//___________________________________________________________
-(void)mDoSomething: (char*)cstr
{
NSLog(@"%s",cstr);
printf("%sn", cstr);
}
//___________________________________________________________
-(void) mPostError: (char *)cstr
{
char cMessage[64] = "Critical Error";
NSString *message = [NSString stringWithCString:cMessage encoding:NSASCIIStringEncoding];
NSString *infoText = [NSString stringWithCString:cstr encoding:NSASCIIStringEncoding];
NSAlert* mAlert = [[NSAlert alloc] init];
[mAlert setMessageText:message];
[mAlert setInformativeText:infoText];
[mAlert setAlertStyle: NSCriticalAlertStyle];
[mAlert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
 [self mDoSomething: cstr]; //this is the suspect line!
}];
}

您的问题与 C 字符串无关,而是与可变生存期有关。

您的执行流程如下:

  1. buttonPressed:被称为。这将创建一个局部变量cstr并调用mPostError:

  2. mPostError:被称为。这调用beginSheetModalForWindow:completionHandler:传递它一个块。该块引用cstr因此复制了cstr中的值,该值是buttonPressed:同名的局部变量的地址。

  3. mPostError:回报,让我们回到...

  4. buttonPressed:,它反过来返回破坏其局部变量cstr。传递给beginSheetModalForWindow:completionHandler:的块现在具有指向释放内存的地址。

  5. 用户消除警报并调用阻止。该块调用mDoSomething:传递现在无效的地址。

  6. mDoSomething:调用printf,它试图将传递地址的内存解释为 C 字符串,但现在内存已被重新用于其他事情,它会产生你看到的废话。

传递 C 字符串不是问题,但传递给块的任何值(在本例中为 char * 值)在执行块时都必须有效。

您可以通过将cstr设置为全局变量来"修复"示例中的问题:

char cstr[256]; // global
- (IBAction)buttonPressed:(NSButton *)sender {
   strcpy(cstr, "Application must quit");

现在cstr始终存在,因此块复制的地址始终有效。

当然,您现在也永久使用了 256 字节的内存,如果此字符串仅在短时间内需要,那就是(小)浪费。

在您的实际代码中,这可能不是问题 - C 字符串可能已经管理好,因此它们根据需要存在。如果不是并且需要使用 C 字符串,那么您可能需要考虑动态分配它们(请参阅malloc和朋友)并在不再需要时释放它们(请参阅free)。

最新更新