ios - Memleak 与 UIDocumentPickerViewController 通过 SwiftUI

标签 ios swift memory-leaks swiftui uidocumentpickerviewcontroller

该应用程序是为 iOS 构建的。我希望用户从它的目录中选择一个文件并获取所选文件的 url。 Apple 推荐使用 UIDocumentPickerViewController 来解决这个问题。我将它包装在 UIViewControllerRepresentable 中,所以它是第一个位置,我可能会弄错

import SwiftUI
import MobileCoreServices

struct DocumentPickerView: UIViewControllerRepresentable {
    func makeCoordinator() -> Coordinator {
        return DocumentPickerView.Coordinator()
    }

    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        let picker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeItem)], in: .import)
        picker.allowsMultipleSelection = false
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {
    }

    class Coordinator: NSObject, UIDocumentPickerDelegate {
        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            print("url: \(urls[0].absoluteString )")
        }
    }
}

struct ContentView: View {
    @State var showPicker = false

    var body: some View {
        Button(action: {
            self.showPicker.toggle()
        }, label: {
            Text("Push me")
        })
        .sheet(isPresented: self.$showPicker) {
            DocumentPickerView()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我认为存在问题的原因是 Xcode 仪器显示内存泄漏,这里是跟踪:

 32 Bytes  100.0%   1       start
 32 Bytes  100.0%   1        main
 32 Bytes  100.0%   1         UIApplicationMain
 32 Bytes  100.0%   1          GSEventRunModal
 32 Bytes  100.0%   1           CFRunLoopRunSpecific
 32 Bytes  100.0%   1            __CFRunLoopRun
 32 Bytes  100.0%   1             __CFRunLoopDoObservers
 32 Bytes  100.0%   1              __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
 32 Bytes  100.0%   1               _afterCACommitHandler
 32 Bytes  100.0%   1                _cleanUpAfterCAFlushAndRunDeferredBlocks
 32 Bytes  100.0%   1                 _runAfterCACommitDeferredBlocks
 32 Bytes  100.0%   1                  __56-[UIPresentationController runTransitionForCurrentState]_block_invoke.478
 32 Bytes  100.0%   1                   _UIViewControllerTransitioningRunCustomTransition
 32 Bytes  100.0%   1                    +[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:]
 32 Bytes  100.0%   1                     ___UIViewControllerTransitioningRunCustomTransition_block_invoke.648
 32 Bytes  100.0%   1                      +[UIInputResponderController _pinInputViewsForInputResponderController:onBehalfOfResponder:duringBlock:]
 32 Bytes  100.0%   1                       ___UIViewControllerTransitioningRunCustomTransition_block_invoke_2
 32 Bytes  100.0%   1                        -[UIViewControllerBuiltinTransitionViewAnimator animateTransition:]
 32 Bytes  100.0%   1                         -[UITransitionView transition:fromView:toView:removeFromView:]
 32 Bytes  100.0%   1                          -[UITransitionView _startTransition:withDuration:]
 32 Bytes  100.0%   1                           +[UIView(UIViewAnimationWithBlocks) conditionallyAnimate:withAnimation:layout:completion:]
 32 Bytes  100.0%   1                            __50-[UITransitionView _startTransition:withDuration:]_block_invoke.169
 32 Bytes  100.0%   1                             +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:]
 32 Bytes  100.0%   1                              +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:]
 32 Bytes  100.0%   1                               +[UIViewAnimationState popAnimationState]
 32 Bytes  100.0%   1                                -[UIViewAnimationState pop]
 32 Bytes  100.0%   1                                 -[UIViewAnimationState _runAlongsideAnimations]
 32 Bytes  100.0%   1                                  __63+[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:]_block_invoke
 32 Bytes  100.0%   1                                   -[_UIViewControllerTransitionContext __runAlongsideAnimations]
 32 Bytes  100.0%   1                                    -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:]
 32 Bytes  100.0%   1                                     __54-[_UISheetPresentationController transitionWillBegin:]_block_invoke.345
 32 Bytes  100.0%   1                                      +[UIView(Animation) performWithoutAnimation:]
 32 Bytes  100.0%   1                                       __54-[_UISheetPresentationController transitionWillBegin:]_block_invoke_2
 32 Bytes  100.0%   1                                        -[_UISheetLayoutInfo _layout]
 32 Bytes  100.0%   1                                         -[_UISheetPresentationController _sheetLayoutInfoLayout:]
 32 Bytes  100.0%   1                                          -[UIView(Hierarchy) layoutBelowIfNeeded]
 32 Bytes  100.0%   1                                           CA::Layer::layout_if_needed(CA::Transaction*)
 32 Bytes  100.0%   1                                            -[CALayer layoutSublayers]
 32 Bytes  100.0%   1                                             -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
 32 Bytes  100.0%   1                                              @objc _UIHostingView.layoutSubviews()
 32 Bytes  100.0%   1                                               _UIHostingView.layoutSubviews()
 32 Bytes  100.0%   1                                                ViewRendererHost.render(interval:updateDisplayList:)
 32 Bytes  100.0%   1                                                 closure #1 in ViewRendererHost.render(interval:updateDisplayList:)
 32 Bytes  100.0%   1                                                  closure #1 in closure #1 in ViewRendererHost.render(interval:updateDisplayList:)
 32 Bytes  100.0%   1                                                   ViewGraph.updateOutputs(at:)
 32 Bytes  100.0%   1                                                    closure #1 in ViewGraph.updateOutputs(at:)
 32 Bytes  100.0%   1                                                     ViewGraph.runTransaction(in:)
 32 Bytes  100.0%   1                                                      AG::Subgraph::update(unsigned int)
 32 Bytes  100.0%   1                                                       AG::Graph::update_attribute(unsigned int, bool)
 32 Bytes  100.0%   1                                                        AG::Graph::UpdateStack::update()
 32 Bytes  100.0%   1                                                         partial apply
 32 Bytes  100.0%   1                                                          protocol witness for static UntypedAttribute._update(_:graph:attribute:) in conformance PlatformViewChild<A>
 32 Bytes  100.0%   1                                                           PlatformViewChild.update(context:)
 32 Bytes  100.0%   1                                                            closure #1 in PlatformViewChild.update(context:)
 32 Bytes  100.0%   1                                                             swift_weakInit
 32 Bytes  100.0%   1                                                              swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::formWeakReference()
 32 Bytes  100.0%   1                                                               operator new(unsigned long)
 32 Bytes  100.0%   1                                                                malloc

在分配时多次调用我有这样的实体失去了他们的内存:

Category                                            Persistent  # Persistent    # Transient Total Bytes
UIDocumentBrowserViewController                     22,50 KiB       15              0       22,50 KiB
DOCRemoteViewController                             22,50 KiB       15              0       22,50 KiB
_UIResilientRemoteViewContainerViewController       13,36 KiB       15              0       13,36 KiB
_UIRemoteView                                       6,80 KiB        15              0       6,80 KiB
_UISizeTrackingView                                 6,56 KiB        15              0       6,56 KiB
DOCRemoteBarButtonTrackingView                      5,62 KiB        15              0       5,62 KiB
DOCConfiguration                                    2,58 KiB        15              0       2,58 KiB
DOCRemoteContext                                    1,88 KiB        15              0       1,88 KiB
_UIViewServiceInterface                             1,41 KiB        15              0       1,41 KiB
__NSXPCInterfaceProxy__NSExtensionContextVending    1,17 KiB        15              0       1,17 KiB
UISystemDefaultTextInputAssistantItem               1,17 KiB        15              0       1,17 KiB
UIKeyboardBIUImageGenerator                         960 Bytes       15              0       960 Bytes
_UIViewServiceReplyControlProxy                     720 Bytes       15              0       720 Bytes
Swift.__SwiftDeferredNSArray                        480 Bytes       15              0       480 Bytes
DOCRemoteBarButton                                  480 Bytes       15              0       480 Bytes
NSWeakObjectValue                                   480 Bytes       15              0       480 Bytes
_UIRemoteViewService                                480 Bytes       15              0       480 Bytes
_UIViewServiceImplicitAnimationEncodingProxy        480 Bytes       15              0       480 Bytes
DOCWeakProxy                                        240 Bytes       15              0       240 Bytes

更新:

我刚开始feedback解决这个问题,但是我发现没有类似的问题,所以如果你有同样的问题也请报告

最佳答案

这绝对是 Apple 的错误,因此值得向他们提交反馈。

同时,这里有一个适用于大多数用例的安全解决方法。使用 Xcode 11.4/iOS 13.4 进行测试。这个想法是使用共享文档选择器实例,并在每次调用时重新配置它。

struct DocumentPickerView: UIViewControllerRepresentable {
    static let picker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeItem)], in: .import)

    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        Self.picker.allowsMultipleSelection = false
        Self.picker.delegate = context.coordinator
        return Self.picker
    }

    // ... other code no changes

关于ios - Memleak 与 UIDocumentPickerViewController 通过 SwiftUI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62469453/

相关文章:

arrays - 可以使用内联语法有条件地将元素添加到数组吗?

实现 Stack 类时出现 C++ 内存泄漏错误

ios - 预期返回 'UITableViewCell' 的函数中缺少返回值,但实际上返回了两次

ios - 使用 UIDocumentBrowserViewController 选择文件夹

ios - 如何使用带有 Xcode 7.1.1 的 iOS 9 创建 LaunchImage?

ios - UIlabel Sizetofit 异常行为

ios - 没有这样的模块 'FIRStorageMetadata'

ios - 为不同的 UITableViewCell 类创建单个 .xib

objective-c - 分析和仪器

ios - NSArray 或 NSMutableArray 的 removeAllObjects 方法是否释放内存?