简体   繁体   English

NSApplication -runModalSession崩溃

[英]NSApplication -runModalSession Crash

I am trying to process files loaded by the user via NSOpenPanel and then start a modal session in order to display the progress on my custom window class. 我试图处理用户通过NSOpenPanel加载的文件,然后启动一个模式会话,以便在自定义窗口类上显示进度。 At the first few dozen tries, it was running well until I've started running into random crashes. 在最初的几十次尝试中,它运行良好,直到我开始遇到随机崩溃。 All objects I've allocated were released after each iteration, so I'm a bit stumped on how to proceed from here. 我分配的所有对象都是在每次迭代后释放的,因此我对如何从此处开始感到有些困惑。 Here's a code snippet for posterity: 这是后代的代码片段:

NSModalSession m = [[NSApplication sharedApplication] beginModalSessionForWindow:progWindow];

while(index < [validImageURLS count])
{
    // Check if user presses the stop track button in pop up window
    if([[NSApplication sharedApplication] runModalSession:m] != NSModalResponseContinue)
    {
        stopProcessing = YES;
        break;
    }

    NSURL* current = [validImageURLS objectAtIndex:index];
    CGImageSourceRef imgSrc = CGImageSourceCreateWithURL((__bridge CFURLRef)current, NULL);
    CGImageRef image = CGImageSourceCreateImageAtIndex(imgSrc, 0, NULL);
    size_t img_width = CGImageGetWidth(image);
    size_t img_height = CGImageGetHeight(image);
    size_t new_size = 0.0;

    if(img_width > img_height)
    {
        new_size = img_width;
    }
    else
    {
        new_size = img_height;
    }

    float aspectRatio = img_width / (float)img_height;

    if(img_width >= img_height)
    {
        img_width  = (int)(round(log2((int)img_width)));
        img_width  = (int)(pow(2.0, img_width));

        if(img_width > 256)
        {
            img_width  = 256;
        }

        img_height = (int)(img_width / aspectRatio);
    }
    else
    {
        img_height = (int)(round(log2((int)img_height)));
        img_height = (int)(pow(2.0, img_height));

        if(img_height > 256)
        {
            img_height = 256;
        }

        img_width  = (int)(img_height * aspectRatio);
    }

    new_size = (int)(round(log2((int)new_size)) + 1);
    new_size = (int)(pow(2.0, new_size));

    GLubyte* imgDataBuffer = new GLubyte[img_width * img_height * ATLAS_NUM_CHANNELS];
    memset(imgDataBuffer, 0, img_width * img_height * ATLAS_NUM_CHANNELS);

    CGColorSpaceRef tmpColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef imgContext = CGBitmapContextCreate(imgDataBuffer,
                                                    img_width,
                                                    img_height,
                                                    8,
                                                    img_width * 4,
                                                    tmpColorSpace,
                                                    kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);

    // Initialize bitmap context attributes
    CGContextSetBlendMode(imgContext, kCGBlendModeNormal);
    CGContextSetInterpolationQuality(imgContext, kCGInterpolationHigh);

    CGRect _c = CGRectMake(0.0, 0.0, img_width, img_height);

    CGContextDrawImage(imgContext, _c, image);

    //flip back
    CGImageRef drawnImg = CGBitmapContextCreateImage(imgContext);

    NSString* c_p = [current path];
    std::string www = std::string([c_p UTF8String]);

    NSString* c_img_name = [current lastPathComponent];
    std::string zzz = std::string([c_img_name UTF8String]);

    // Creating new input content to be put into to the input list
    // for BinPack2D to process later
    SquareContent mycontent(www);
    mycontent.img = drawnImg;

    inputContent += BinPack2D::Content<SquareContent>(mycontent,
                                                      BinPack2D::Coord(),
                                                      BinPack2D::Size((int)img_width, (int)img_height),
                                                      false);

    CGContextRelease(imgContext);
    CGColorSpaceRelease(tmpColorSpace);
    CGImageRelease(image);
    CFRelease(imgSrc);
    delete [] imgDataBuffer;


    // Update progress bar view in our pop up window
    [self UpdateTrackProgressAsync:@[[NSNumber numberWithDouble:index],
                                     [NSNumber numberWithDouble:(int)[validImageURLS count]]]];

    index++;
}

[[NSApplication sharedApplication] endModalSession:m];

Below is a screenshot of the said error: 以下是上述错误的屏幕截图:

在此处输入图片说明

I simply followed the structure that was presented in Apple's documentation: https://developer.apple.com/documentation/appkit/nsapplication/1428590-runmodalsession?language=objc 我只是遵循Apple文档中介绍的结构: https : //developer.apple.com/documentation/appkit/nsapplication/1428590-runmodalsession?language=objc

Reason why I need to run this via a modal session for window instead of dispatching the code in a background queue is because I'm developing this for a plugin. 之所以需要通过窗口的模式会话而不是在后台队列中分派代码来运行它,是因为我正在为插件开发它。 I want the processing to halt all other UI in the host application until the work has been finished, or the user presses a button that will trigger a stopModal call. 我希望处理停止主机应用程序中的所有其他UI,直到工作完成,或者用户按下将触发stopModal调用的按钮。 I hope someone can point me in the right direction. 我希望有人能指出我正确的方向。 Thank you and many cheers. 谢谢你,也很高兴。

run with zombies enabled to narrow down cause of crash. 在启用僵尸的情况下运行以缩小崩溃原因。 (Edit scheme... diagnostics... zombie objects checkbox) (编辑方案...诊断...僵尸对象复选框)

what happened to me was that endModalSession crashed when it got called recursively. 我发生的事情是endModalSession在递归调用时崩溃了。 No idea what's causing your problem. 不知道是什么原因导致了您的问题。

Here's some (maybe unrelated) problems I encountered: 这是我遇到的一些(可能不相关)问题:

  • ensure there's no recursion going on 确保没有递归进行
  • see endModalSession subfunction below to avoid recursive call 请参阅下面的endModalSession子函数,以避免递归调用
  • be sure your app is the main app and your window is the key window 确保您的应用程序是主应用程序,并且您的窗口是关键窗口

Here's some source code FWIW 这是一些源代码FWIW

- (void)endModalSession
{
   if ( modalSession )
   {
      NSModalSession tempSession = modalSession;
      modalSession = NULL;
      [NSApp endModalSession:tempSession];
   }
   appInactiveWait = NO;
}

- (BOOL) waiting { return (modalSession || appInactiveWait); }

- (void)foo
{
   while ( !done )
   {
      // Only start a modal session when app is active.
      // Otherwise you get spurious Dock bouncing when you switch between Finder and this app.
      // Particulary, Remote afp mounts send spurious FSEventStream events every 30 seconds.
      if ( [NSApp isActive] && !startedModal )
      {
         if ( [NSApp keyWindow] == remoteFileManagerWindow || [NSApp keyWindow] == [NSApp mbWindow] )
         {
            startedModal = YES;
            //DLog( @"startingModal (RemoteFileManager) cookie %d", task.readWriteCookie );
            modalSession = [NSApp beginModalSessionForWindow:remoteFileManagerWindow];
            appInactiveWait = NO;
            //DLog( @"startedModal (RemoteFileManager) cookie %d", task.readWriteCookie );
         }
         else
         {
            DLog( @"deferring modal (RemoteFileManager) to keyWindow %@", [NSApp keyWindow] );
         }
      }

      if ( ![self waiting] )
         break;

      if (blah blah blah )
         done = true;

      if ( [self waiting] )
      {             
         if ( modalSession )
         {
            if ([NSApp runModalSession:modalSession] != NSModalResponseContinue)
               break;
         }
         else
         {
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
         }
      }

      [self processDelayedQueue];
   }
   [self setCurrentTask:nil];

   [self endModalSession];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM