UIImagePickerController多次替换图片后崩溃报错求助
问题现象
使用UIImagePickerController的编辑功能时,替换图片5-6次后程序崩溃,抛出以下错误:
<0x100d08660> Gesture: System gesture gate timed out.
[u 0A3027E7-E344-4002-9629:m (null)] [com.apple.mobileslideshow.photo-picker(1.0)] Connection to plugin interrupted while in use.
[u 0A3027E7-E344-4002-9629:m (null)] [com.apple.mobileslideshow.photo-picker(1.0)] Connection to plugin invalidated while in use.
-[PUPhotoPickerHostViewController viewServiceDidTerminateWithError:] Error Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
相关代码如下:
import SwiftUI struct ImagePicker: UIViewControllerRepresentable { var sourceType: UIImagePickerController.SourceType = .photoLibrary @Binding var selectedImage: UIImage @Binding var showSheet: Bool func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController { let imagePicker = UIImagePickerController() imagePicker.allowsEditing = true imagePicker.sourceType = sourceType imagePicker.delegate = context.coordinator let appearance = UINavigationBarAppearance() appearance.configureWithTransparentBackground() appearance.backgroundColor = .systemBackground appearance.titleTextAttributes = [.foregroundColor: UIColor.systemBackground] appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.systemBackground] imagePicker.navigationBar.tintColor = .white imagePicker.navigationBar.standardAppearance = appearance imagePicker.navigationBar.compactAppearance = appearance imagePicker.navigationBar.scrollEdgeAppearance = appearance return imagePicker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) { } func makeCoordinator() -> Coordinator { Coordinator(self) } final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { var parent: ImagePicker init(_ parent: ImagePicker) { self.parent = parent } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage { parent.selectedImage = image } parent.showSheet = false } } } struct ImagePickerTest: View { @State private var image = UIImage() @State private var showSheet = false var body: some View { VStack { Button("Change photo") { showSheet = true } Image(uiImage: self.image) .resizable() .frame(width: 100, height: 100) .background(Color.gray) .scaledToFit() .clipShape(Circle()) } .sheet(isPresented: $showSheet) { ImagePicker(sourceType: .photoLibrary, selectedImage: $image, showSheet: $showSheet) .ignoresSafeArea(.all) } } }
问题原因
- 未正确销毁Picker实例:原代码仅通过修改
showSheet绑定关闭Sheet,但未主动调用UIImagePickerController的dismiss方法,导致Picker实例无法被正确销毁,插件进程资源持续占用,多次重复后触发连接中断。 - 循环引用风险:Coordinator强持有
parent(ImagePicker实例),可能引发循环引用,导致内存泄漏,进一步加剧资源消耗。 - 缺少取消处理逻辑:未实现
imagePickerControllerDidCancel代理方法,用户取消选择时Picker同样无法被正确清理。
修复方案
1. 修改Coordinator的引用方式并完善代理方法
将Coordinator中对parent的引用改为弱引用,并实现取消选择的处理逻辑,确保Picker被正确dismiss:
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { // 改为弱引用避免循环引用 weak var parent: ImagePicker? init(_ parent: ImagePicker) { self.parent = parent } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let image = info[.editedImage] as? UIImage { parent?.selectedImage = image } // 先dismiss Picker,再更新绑定状态 picker.dismiss(animated: true) { self.parent?.showSheet = false } } // 实现取消选择的代理方法 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) { self.parent?.showSheet = false } } }
2. 确保Sheet的正确销毁
SwiftUI的sheet在isPresented变为false时会自动销毁内部的ViewController,但配合主动调用Picker的dismiss,可以确保资源被及时释放,避免插件进程过载。
额外说明
PhotosPicker基于新的PhotosUI框架,采用了更轻量的进程管理机制,因此不会出现此类问题,但它确实缺乏UIImagePickerController的内置编辑功能。上述修改通过规范内存管理和Picker生命周期,即可解决重复编辑后的崩溃问题。
内容的提问来源于stack exchange,提问作者MobileFirst

