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

时间:2015-01-09 18:30:28

标签: objective-c macos cocoa

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

-from [buttonPressed]

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] @Èø_ˇ
@\351\277_\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("%s\n", 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!
}];
}

1 个答案:

答案 0 :(得分:0)

您的问题与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字符串,但是现在该内存已被重新用于其他内容并且它产生了你看到的废话。

  7. 传递C字符串不是问题,但是传递给块的任何值(在这种情况下为char *值)必须在执行块时有效。

    你可以修复"您的示例中的问题是将cstr设为全局变量:

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

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

    当然,你现在也永久地使用了256字节的内存,如果这个字符串只需要很短的一段时间,那就是(小)浪费。

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

    HTH