diff --git a/mediapipe/tasks/ios/vision/image_classifier/sources/MPPImageClassifier.mm b/mediapipe/tasks/ios/vision/image_classifier/sources/MPPImageClassifier.mm index 45daa6db0..bb9b71e0e 100644 --- a/mediapipe/tasks/ios/vision/image_classifier/sources/MPPImageClassifier.mm +++ b/mediapipe/tasks/ios/vision/image_classifier/sources/MPPImageClassifier.mm @@ -85,22 +85,34 @@ static NSString *const kTaskGraphName = if (options.imageClassifierDelegate) { _imageClassifierDelegate = options.imageClassifierDelegate; + + // Capturing `self` as weak in order to avoid `self` being kept in memory + // and cause a retain cycle, after self is set to `nil`. + MPPImageClassifier *__weak weakSelf = self; + dispatch_queue_t callbackQueue = + dispatch_queue_create("com.mediapipe.tasks.imageClassifierCallbackQueue", NULL); packetsCallback = [=](absl::StatusOr status_or_packets) { - NSError *callbackError = nil; - if (![MPPCommonUtils checkCppError:status_or_packets.status() toError:&callbackError]) { - if ([_imageClassifierDelegate - respondsToSelector:@selector - (imageClassifier: - didFinishClassificationWithResult:timestampInMilliseconds:error:)]) { - [_imageClassifierDelegate imageClassifier:self - didFinishClassificationWithResult:nil - timestampInMilliseconds:Timestamp::Unset().Value() - error:callbackError]; - } + // Check to ensure that the delegate method is not called on a nil object + // leading to a segmentation fault, we check `weakSelf` is `nil` before + // performing any processing. + if (!weakSelf || + ![weakSelf.imageClassifierDelegate + respondsToSelector:@selector + (imageClassifier: + didFinishClassificationWithResult:timestampInMilliseconds:error:)]) { return; } - PacketMap &outputPacketMap = status_or_packets.value(); + NSError *callbackError = nil; + if (![MPPCommonUtils checkCppError:status_or_packets.status() toError:&callbackError]) { + [weakSelf.imageClassifierDelegate imageClassifier:weakSelf + didFinishClassificationWithResult:nil + timestampInMilliseconds:Timestamp::Unset().Value() + error:callbackError]; + return; + } + + PacketMap outputPacketMap = status_or_packets.value(); if (outputPacketMap[kImageOutStreamName.cppString].IsEmpty()) { return; } @@ -109,18 +121,14 @@ static NSString *const kTaskGraphName = [MPPImageClassifierResult imageClassifierResultWithClassificationsPacket: outputPacketMap[kClassificationsStreamName.cppString]]; - if ([_imageClassifierDelegate - respondsToSelector:@selector - (imageClassifier: - didFinishClassificationWithResult:timestampInMilliseconds:error:)]) { - [_imageClassifierDelegate imageClassifier:self - didFinishClassificationWithResult:result - timestampInMilliseconds:outputPacketMap[kImageOutStreamName.cppString] - .Timestamp() - .Value() / - kMicroSecondsPerMilliSecond - error:callbackError]; - } + [weakSelf.imageClassifierDelegate + imageClassifier:weakSelf + didFinishClassificationWithResult:result + timestampInMilliseconds:outputPacketMap[kImageOutStreamName.cppString] + .Timestamp() + .Value() / + kMicroSecondsPerMilliSecond + error:callbackError]; }; } diff --git a/mediapipe/tasks/ios/vision/object_detector/sources/MPPObjectDetector.mm b/mediapipe/tasks/ios/vision/object_detector/sources/MPPObjectDetector.mm index fbac6f00d..8c8b0c3bb 100644 --- a/mediapipe/tasks/ios/vision/object_detector/sources/MPPObjectDetector.mm +++ b/mediapipe/tasks/ios/vision/object_detector/sources/MPPObjectDetector.mm @@ -81,17 +81,23 @@ static NSString *const kTaskGraphName = @"mediapipe.tasks.vision.ObjectDetectorG if (options.objectDetectorDelegate) { _objectDetectorDelegate = options.objectDetectorDelegate; + + // Capturing `self` as weak in order to avoid `self` being kept in memory + // and cause a retain cycle, after self is set to `nil`. + MPPObjectDetector *__weak weakSelf = self; packetsCallback = [=](absl::StatusOr statusOrPackets) { + if ([!weakSelf && weakSelf.objectDetectorDelegate + respondsToSelector:@selector + (objectDetector:didFinishDetectionWithResult:timestampInMilliseconds:error:)]) { + return; + } + NSError *callbackError = nil; if (![MPPCommonUtils checkCppError:statusOrPackets.status() toError:&callbackError]) { - if ([_objectDetectorDelegate - respondsToSelector:@selector - (objectDetector:didFinishDetectionWithResult:timestampInMilliseconds:error:)]) { - [_objectDetectorDelegate objectDetector:self - didFinishDetectionWithResult:nil - timestampInMilliseconds:Timestamp::Unset().Value() - error:callbackError]; - } + [_objectDetectorDelegate objectDetector:weakSelf + didFinishDetectionWithResult:nil + timestampInMilliseconds:Timestamp::Unset().Value() + error:callbackError]; return; } @@ -103,17 +109,15 @@ static NSString *const kTaskGraphName = @"mediapipe.tasks.vision.ObjectDetectorG MPPObjectDetectionResult *result = [MPPObjectDetectionResult objectDetectionResultWithDetectionsPacket:statusOrPackets.value()[kDetectionsStreamName .cppString]]; - if ([_objectDetectorDelegate - respondsToSelector:@selector - (objectDetector:didFinishDetectionWithResult:timestampInMilliseconds:error:)]) { - [_objectDetectorDelegate objectDetector:self - didFinishDetectionWithResult:result - timestampInMilliseconds:outputPacketMap[kImageOutStreamName.cppString] - .Timestamp() - .Value() / - kMicroSecondsPerMilliSecond - error:callbackError]; - } + + [weakSelf.objectDetectorDelegate + objectDetector:weakSelf + didFinishDetectionWithResult:result + timestampInMilliseconds:outputPacketMap[kImageOutStreamName.cppString] + .Timestamp() + .Value() / + kMicroSecondsPerMilliSecond + error:callbackError]; }; }