UIDocumentPickerViewController通过SwiftUI存在内存泄漏问题。

3

该应用程序是为iOS构建的。我希望用户可以从其目录中选择文件并获取所选文件的URL。苹果建议使用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

更新:

我刚刚开始使用反馈来解决这个问题,但我发现没有类似的问题,如果您也有同样的问题,请报告一下。

1个回答

0

这绝对是苹果的漏洞,值得向他们提交反馈。

同时,这里有一个安全的解决方法适用于大多数情况。已经测试过 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接