简体   繁体   English

swift 合并接收器接收值 memory 泄漏

[英]swift combine sink receiveValue memory leak

I'm having a hard time with dealing with Combine.我很难与Combine打交道。 After the publisher is complete I want to update a value but whenever I update that value the memory is allocated and never goes away.发布者完成后,我想更新一个值,但每当我更新该值时,memory 就会被分配并且永远不会消失。

Whenever I try to assign image there is a leak.每当我尝试分配图像时,都会出现泄漏。 If I don't assign no leak.如果我不指定不泄漏。

EDIT: Reproducible example here: https://github.com/peterwarbo/MemoryAllocation编辑:此处可重现的示例: https://github.com/peterwarbo/MemoryAllocation

This is what my code looks like:这就是我的代码的样子:

final class CameraController: ObservableObject {

    private var storage = Set<AnyCancellable>()    
    var image: UIImage?

    func capture(_ image: UIImage) {

        PhotoLibrary.saveImageToTemporaryDirectory(image) // AnyPublisher<URL, Error>
            .zip(PhotoLibrary.saveImage(image, location: self.locationObserver.location) // AnyPublisher<UIImage, Error>)
            .sink(receiveCompletion: { [weak self] (completion) in
                switch completion {
                case let .failure(error):
                    Log.error(error)
                    self?.handleCaptureError(error)
                case .finished: break
                }
            }) { [weak self] (value) in
                print(value.1) // no leak
                self.image = value.1 // leak

            }
            .store(in: &self.storage)
     }
}

I've also tried instead of using sink :我也尝试过而不是使用sink

.receive(
    subscriber: Subscribers.Sink(
        receiveCompletion: { [weak self] completion in
            switch completion {
            case let .failure(error):
                Log.error(error)
                self?.handleCaptureError(error)
            case .finished: break
            }
        },
        receiveValue: { value in
            print(value.1) // no leak
            self.image = value.1 // leak            
        }
    )
)

An obvious problem with your code is that you create and store a new pipeline every time capture is called.代码的一个明显问题是每次调用capture时都会创建并存储一个新管道。 That is the opposite of how to use Combine;这与使用 Combine 的方式相反; you might as well not be using Combine at all.您可能根本不使用 Combine。 The way to use Combine is to create a pipeline once and let information come down the pipeline asynchronously from then on.使用Combine 的方法是创建一次管道,然后让信息从管道异步传出。

You posted an example project in which you use a Future to introduce a delay in the passing of an image down a pipeline.您发布了一个示例项目,在该项目中,您使用 Future 来引入图像沿管道传递的延迟。 In your project, the user chooses an image from the photo library, repeatedly.在您的项目中,用户从照片库中反复选择一张图片。 Once again, in your project you create and store a new pipeline every time an image is chosen.再一次,在您的项目中,每次选择图像时,您都会创建并存储一个新的管道。 I rewrote the example as follows:我将示例改写如下:

import UIKit
import Combine

class ViewController: UIViewController, UINavigationControllerDelegate {
    let queue = DispatchQueue(label: "Queue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
    var image: UIImage?
    var storage: Set<AnyCancellable> = []
    let publisher = PassthroughSubject<UIImage, Never>()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.publisher
            .flatMap {image in
                self.futureMaker(image: image)
            }
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { (completion) in
            }) { (value) in
                print("finished processing image")
                self.image = value
            }
            .store(in: &self.storage)
    }
    @IBAction func didTapPickImage(_ sender: UIButton) {
        let picker = UIImagePickerController()
        picker.delegate = self
        present(picker, animated: true)
    }
    func futureMaker(image: UIImage) -> AnyPublisher<UIImage, Never> {
        Future<UIImage, Never> { promise in
            self.queue.asyncAfter(deadline: .now() + 0.5) {
                promise(.success(image))
            }
        }.eraseToAnyPublisher()
    }
}
extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        dismiss(animated: true)
        guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
        print("got image")
        self.publisher.send(image)
    }
}

Note the architecture: I create the pipeline once, in viewDidLoad , and whenever an image arrives I pass it down the same pipeline.请注意架构:我在viewDidLoad中创建了一次管道,每当图像到达时,我都会将其传递到同一个管道中。 Some memory is used, to be sure, because we are storing a UIImage;可以肯定的是,使用了一些 memory,因为我们存储的是 UIImage; but it does not grow in any uncontrolled way, but levels off in an optimal manner.但它不会以任何不受控制的方式增长,而是以最佳方式趋于平稳。

在此处输入图像描述

We're using 8.4 MB after picking all the images in the library repeatedly.在反复挑选库中的所有图像后,我们使用了 8.4 MB。 No problem there!那里没问题!

在此处输入图像描述

Also, no surplus large images are persisting.此外,没有多余的大图像持续存在。 Looking at memory that comes from choosing in the image picker, one image persists;在图像选择器中查看来自选择的 memory ,一个图像仍然存在; that is 2.7 MB of our 8.4 MB:这是我们 8.4 MB 中的 2.7 MB:

在此处输入图像描述

That is exactly what we expect.这正是我们所期望的。

Just by code reading... use weak self, not direct self,只是通过代码阅读......使用弱自我,而不是直接自我,

}) { [weak self] (value) in
    print(value.1) // no leak
    self?.image = value.1     // << here !!
    self?.storage.removeAll() // just in case
}

also I would add delivery on main queue, as我也将在主队列中添加交付,如

PhotoLibrary.saveImageToTemporaryDirectory(image)
    .zip(PhotoLibrary.saveImage(image, location: self.locationObserver.location)
    .receive(on: DispatchQueue.main)          // << here !!
    .sink(receiveCompletion: { [weak self] (completion) in
    // ... other code here

You are using.store(in: &self.storage)您正在使用.store(in: &self.storage)

need to cancel this private var storage = Set()需要取消这个私有 var storage = Set()

storage.cancel() storage.removeAll() storage.cancel() storage.removeAll()

and also self need to be weak自己也要软弱

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM