Qt iOS app crash in completion handler callback with EXC_BAD_ACCESS

109 views Asked by At

I'm writing a Qt app on iOS which uses the MLKit FaceDetector. I have a FaceDetector class which has the following method:

void FaceDetector::runFaceDetection(const QImage &image)
{
  // initialize a MLKFaceDetector 
  [mlkFaceDetector processImage:visionImage
                      completion:^(NSArray<MLKFace *> *_Nullable faces, NSError *_Nullable error) {

       if (error != nil) {
           // process error
       }

       DetectionResults results; // DetectionResults is just another of my classes

       // process results using faces

       setResults(results);
       emit detectionComplete(true);
    }];
}

where setResults and detectionComplete are other member function of the FaceDetector

I use the FaceDetector in a FaceDetectionFilter class:

FaceDetectionFilter.h

class FaceDetectionFilter : public QAbstractVideoFilter {
private: 
  FaceDetector        m_FaceDetector;
}

Basically I use the FaceDetectionFilter in my QML with Camera and VideoOutput component, and the VideoOutput is able to detect faces, so far so good.

However, the app occasionally crashes when I close my camera page while the filter is running, i.e. the page is popped out of the app's stackview. XCode shows EXC_BAD_ACCESS error in the setResults or detectionComplete line in the runFaceDetection method.

I think this is due to when the camera page is popped, the FaceDetectionFilter and thus the FaceDetector object is destroyed. But once in a while the completion handler callback is executed after the object destruction, resulting in illegal memory access.

I tried to fix this by adding a m_Initialized boolean member to the FaceDetector class, and setting it to false in the destructor:

FaceDetector ::~FaceDetector ()
{
  m_Initialized = false;
}

and in the runFaceDetection method:

void FaceDetector::runFaceDetection(const QImage &image)
{
  // initialize a MLKFaceDetector 
  [mlkFaceDetector processImage:visionImage
                      completion:^(NSArray<MLKFace *> *_Nullable faces, NSError *_Nullable error) {

       if (!m_Initialized) {
           qDebug() << "FaceDetector destroyed, stop processing";
           return;
       }
       if (error != nil) {
           // process error
       }
       // continue processing
    }];
}

This seems to fix the crash, and I can see that when I close the camera, occasionally the "stop processing" line is printed.

However, very rarely the app still crashes and it seems the m_Initialized guard is not working (but I've only been able to reproduce this once, it's so rare that I'm not even sure if that really happened). I think this rare case occurs when the FaceDetector destruction happens between the m_Initialized guard and the setResults line, therefore making the guard check fail.

My question is: are there any other better way to solve this issue? I've searched online for a while and found some answers from the Objective C side related to the use of __block or the concept of "retain" the object. Or, from C++ side the use of shared_ptr to manage object lifetime. I guess the key is find a way to know if the object itself is destroyed in the completion handler, or am I missing something?

0

There are 0 answers