Делегировать круг удержания, хотя используется слабый

#ios #swift #memory-leaks #delegates

#iOS #swift #утечки памяти #делегаты

Вопрос:

Я работаю с координаторами. Мой ViewController не освобождает выделение, даже если я установил слабые делегаты.

Координатор:

 class JournalDetailCoordinator: Coordinator {
    var dependencys: AppDependency
    var navigationController: UINavigationController
    var collectionViewController: CollectionViewWithMenuController!
    var imagePickerManager: ImagePickerManager!




    init(dependencys: AppDependency, navigationController: UINavigationController) {
        self.dependencys = dependencys
        self.navigationController = navigationController

    }

    func start() {
        loadCollectionViewController()
    }

    deinit {
        print("JournalDetailCoordinator deinitialisiert")

    }

    func loadCollectionViewController() {
        var journalDetailViewControllerContainer = [JournalDetailViewController]()
        for journal in dependencys.journals {
            let vc: JournalDetailViewController = dependencys.getJournalDetailDependency().createVC()
            vc.entryJournal = journal
            vc.delegateLoadImagePickerManager = self
            journalDetailViewControllerContainer.append(vc)
        }
        collectionViewController = dependencys.getCollectionViewWithMenuDependency().createVC()
        collectionViewController.managedViewControllers = journalDetailViewControllerContainer
        navigationController.pushViewController(collectionViewController, animated: true)
    }



}

extension JournalDetailCoordinator: LoadImagePickerManager {
    func loadImagePickerManager<T>(vc: T) where T : UIViewController amp; ImageGetterDelegate {
        imagePickerManager = ImagePickerManager()
        imagePickerManager.delegate = vc
        imagePickerManager.pickImage(viewController: collectionViewController)

    }
}
  

ViewController:

 class JournalDetailViewController: UIViewController {
    lazy var mainView: JournalDetailViewP = {
        let view = JournalDetailViewP()
        return view
    }()

    typealias myType = SetJournal amp; HasImagePickerManager
    // dependency
    var dep: myType!
    var entryJournal: Journaling!



    var tableViewDataSource: JournalDetailTVDataSource?
    var collectionViewInteraction: AddImageCollectionViewInteraction?

    weak var delegateLoadImagePickerManager: LoadImagePickerManager?

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Detail Journal"
        // only for testing without coordinator connection
        //        if entryJournal == nil {
        //            entryJournal = NewJournal()
        //        }
        //        dep = AppDependency()
        setMainView()
        loadTableView()
        loadCollectionView()

    }
    override func viewDidDisappear(_ animated: Bool) {
        print("view did disappear Journal Detail")
    }

    deinit {
        dep.setJournal(newJournal: entryJournal)
        print("JournalDetailViewController deinitialisiert")
    }


    @objc func getImage() {
        delegateLoadImagePickerManager?.loadImagePickerManager(vc: self)
        //        dep.imagePickerManager.delegate = self
        //        dep.imagePickerManager.pickImage(viewController: self)

    }

    func saveEntry() {

    }

}

extension JournalDetailViewController: Storyboarded {}
extension JournalDetailViewController: DependencyInjectionVC {}
extension JournalDetailViewController: SetMainView {}

extension JournalDetailViewController: ImageGetterDelegate {
    func returnImage(image: UIImage) {
        if entryJournal.image[0] ==  nil {
            entryJournal.image[0] = image
        } else {
            entryJournal.image.append(image)
        }

        loadCollectionView()
    }
}

extension JournalDetailViewController: AddImageCollectionViewInteractionDelegate {
    func deleteImage(index: Int) {
    }

    func addImage() {
        getImage()
    }
}
  

Они освобождаются, если я не выполняю функцию getImage(), поэтому я думаю, что это причина круга хранения.

Это ImagePickerManager:

 protocol ImageGetterDelegate: class {
    func returnImage(image: UIImage)
}

class ImagePickerManager: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var imagePicker = UIImagePickerController()
    weak var delegate: ImageGetterDelegate?

    override init() {
        super.init()
        print("ImagePickerManager initialisiert")
    }


    deinit {
        print("imagePickerManager deinitialisiert")
    }
    /// use to pick the Image, make sure to use the root ViewController to pass in to
    func pickImage<T:UIViewController>(viewController: T) {
        let alertList = UIAlertController(title: NSLocalizedString("Load Picture", comment: "Picture alert Alertcontroller"), message: nil, preferredStyle: .actionSheet)

        let cameraAction = UIAlertAction(title: "Camera", style: .default) {
            UIAlertAction in self.openCamera(viewController: viewController)
            alertList.dismiss(animated: true, completion: nil)
        }

        let galleryAction = UIAlertAction(title: "Gallery", style: .default) {
            UIAlertAction in self.openGallery(viewController: viewController)
            alertList.dismiss(animated: true, completion: nil)
        }

        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
            UIAlertAction in
            alertList.dismiss(animated: true, completion: nil)
        }

        alertList.addAction(cameraAction)
        alertList.addAction(galleryAction)
        alertList.addAction(cancelAction)

        viewController.present(alertList, animated: true, completion: nil)
    }

    private func openCamera<T:UIViewController>(viewController: T) {

        if(UIImagePickerController .isSourceTypeAvailable(.camera)) {
            imagePicker.sourceType = .camera
            imagePicker.delegate = self
            viewController.present(imagePicker, animated: true, completion: nil)
        } else {
            let warningAlert = UIAlertController(title: "Warning", message: "You do not have a camera", preferredStyle: .alert)
            let cancelAction = UIAlertAction(title: "Okay", style: .cancel) {
                UIAlertAction in
                warningAlert.dismiss(animated: true, completion: nil)
            }
            warningAlert.addAction(cancelAction)
            viewController.present(warningAlert, animated: true, completion: nil)
        }

    }

    private func openGallery<T:UIViewController>(viewController: T) {
        imagePicker.sourceType = .photoLibrary
        imagePicker.delegate = self
        viewController.present(imagePicker, animated: true, completion: nil)
    }

    @objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true, completion: nil)
        guard let image = info[.originalImage] as? UIImage else {
            print("Expected a dictionary containing an image, but was provided the following: (info)")
            return
        }
        delegate?.returnImage(image: image)

    }

}
  

ImagePickerManager не выделяет после освобождения координатора. Итак, я думаю, что круг хранения связан с тем, что я передаю ViewVontroller обратно координатору в LoadImagePickerManager, а затем устанавливаю vc для координатора? У кого-нибудь есть идея, как решить эту проблему или что делать?

Редактировать: LoadImagePickerManager:

 protocol LoadImagePickerManager: class {
    func loadImagePickerManager<T: UIViewController amp; ImageGetterDelegate>(vc: T)
}
  

Я думаю, что утечка памяти происходит здесь при передаче collectionViewController:

 imagePickerManager.pickImage(viewController: collectionViewController)
  

Поскольку я провел несколько тестов, если я не выполняю эту часть, то все освобождается нормально.

Обновленный класс ImagePickerManager:

 class ImagePickerManager: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var imagePicker = UIImagePickerController()
    weak var delegate: ImageGetterDelegate?
    var viewController: UIViewController!

    override init() {
        super.init()
        print("ImagePickerManager initialisiert")
    }


    deinit {
        print("imagePickerManager deinitialisiert")
    }
    /// use to pick the Image, make sure to use the root ViewController to pass in to
    func pickImage<T:UIViewController>(viewController: T) {
        self.viewController = viewController
        let alertList = UIAlertController(title: NSLocalizedString("Load Picture", comment: "Picture alert Alertcontroller"), message: nil, preferredStyle: .actionSheet)

        let cameraAction = UIAlertAction(title: "Camera", style: .default) {
            UIAlertAction in self.openCamera()
            alertList.dismiss(animated: true, completion: nil)
        }

        let galleryAction = UIAlertAction(title: "Gallery", style: .default) {
            UIAlertAction in self.openGallery()
            alertList.dismiss(animated: true, completion: nil)
        }

        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
            UIAlertAction in
            alertList.dismiss(animated: true, completion: nil)
        }

        alertList.addAction(cameraAction)
        alertList.addAction(galleryAction)
        alertList.addAction(cancelAction)

        viewController.present(alertList, animated: true, completion: nil)
    }

    private func openCamera() {

        if(UIImagePickerController .isSourceTypeAvailable(.camera)) {
            imagePicker.sourceType = .camera
            imagePicker.delegate = self
            viewController.present(imagePicker, animated: true, completion: nil)
        } else {
            let warningAlert = UIAlertController(title: "Warning", message: "You do not have a camera", preferredStyle: .alert)
            let cancelAction = UIAlertAction(title: "Okay", style: .cancel) {
                UIAlertAction in
                warningAlert.dismiss(animated: true, completion: nil)
            }
            warningAlert.addAction(cancelAction)
            viewController.present(warningAlert, animated: true, completion: nil)
        }

    }

    private func openGallery() {
        imagePicker.sourceType = .photoLibrary
        imagePicker.delegate = self
        viewController.present(imagePicker, animated: true, completion: nil)
    }

    @objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true, completion: nil)
        guard let image = info[.originalImage] as? UIImage else {
            print("Expected a dictionary containing an image, but was provided the following: (info)")
            return
        }
        viewController = nil
        delegate?.returnImage(image: image)

    }

}
  

Я добавил переменную ViewController в класс и установил ее с помощью pickImage(), а затем, когда изображение выбрано, я установил переменную равной нулю. Затем UIViewController освобождается, но класс ImagePickerManager по-прежнему остается активным и не выделяется.

Комментарии:

1. Вам следует пересмотреть свой вопрос, чтобы публиковать как можно меньше кода. Прямо сейчас это похоже на стену кода.

2. Спасибо, хорошо, я обновлю вопрос. Проблема в том, что я не знаю точно, в чем проблема..

3. Я думаю, вы можете сократить свой пример кода с помощью минималистичного примера, который воспроизводит проблему и также является отражением вашего кода. Таким образом, больше людей смогут понять вашу проблему и помочь вам. 🙂

4. Проверка AddImageCollectionViewInteractionDelegate слаба или нет в его объекте источника данных.

5. да, все делегаты слабые

Ответ №1:

Поскольку вы используете weak delegate so, это никоим образом не приведет к созданию цикла сохранения.

Я думаю, что ваш ViewController не освобождает выделение, потому что ваш ViewController все еще находится в стеке вашей навигации.

Попробуйте удалить все ViewControllers из стека навигации, и тогда ваш блок освобождения будет работать как обычно.

Попробуйте следующий код в зависимости от вашего требования (присутствует / нажимается), когда вы возвращаетесь к своему HomeViewController:

 self.navigationController?.popToRootViewController(animated: true)

self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
  

Редактировать:

Убедитесь, что ваш протокол относится к классовому типу, тогда будет работать только слабая ссылка.

 protocol LoadImagePickerManager: class {

}
  

В вашем PickerManager попробуйте отклонить, используя следующий код, он перенаправит вас на контроллер rootview, но вы можете снова нажать или представить требуемому viewcontroller:

 self.view.window?.rootViewController?.dismiss(animated: false, completion: nil)
  

Комментарии:

1. спасибо за ваш ответ. Но почему ViewController освобождает выделение, если getImage() не выполняется?

2. Я обновил свой код, да, мой протокол относится к классовому типу. Я также провел еще несколько тестов и обнаружил, что, по моему мнению, утечка памяти происходит при передаче CollectionViewController в ImagePickerManager и использовании его для AlertController.

3. итак, я думаю, что проблема в ImagePickerManager, я добавил переменную ViewController в ImagePickerManager и установил ее равной нулю после выбора изображения, после чего ViewControllers освобождаются. Но сам ImagePickerManager по-прежнему не освобождается, поэтому в этом классе что-то не так, я опубликовал обновленный класс ImagePickerManager. Спасибо за вашу помощь