Tocropviewcontroller: Possible memory leak?

Created on 8 Mar 2018  ·  4Comments  ·  Source: TimOliver/TOCropViewController

I'm using CropViewController for swift and has just installed yesterday so my pod is up to date. I really like it, good job man but i think there is possible memory leak occurred.

I just create new UIViewController and present it via modally. I have UIImageView on this controller and it has tap recognizer. I use UIImagePickerController and present CropViewController object when media picking finish like;

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

        if let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
            let cropVC = CropViewController(croppingStyle: .circular, image: selectedImage)
            cropVC.delegate = self
            picker.present(cropVC, animated: false, completion: nil)
        } 
    }

After finished cropping and dismissing this UIViewController, deinit never called. If i'm changing imagePickerController delegate method like this;

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

        if let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
            self.imageView.image = selectedImage
        } 
    }

then if i'm dismiss, deinit calls. I don't set any speciald items, only UIViewController, UIImageView and UIImagePickerController here. Do i set up anything wrong or is this a memory leak?

- EDIT -

When i call deinit in your CropViewController.swift class, neither it's not called.

bug

Most helpful comment

okay.. I fixed it with the following
1- I made the delegate weak, because delegates should always be weak
2- I made the self calls inside the handlers to be [weak self]

In your CropViewController.swift see the onDid... callbacks.

fileprivate func setUpDelegateHandlers() {
        guard let delegate = self.delegate else {
            onDidCropToRect = nil
            onDidCropImageToRect = nil
            onDidCropToCircleImage = nil
            onDidFinishCancelled = nil
            return
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didCropImageToRect:angle:))) {
            self.onDidCropImageToRect = {[weak self] rect, angle in
                delegate.cropViewController!((self)!, didCropImageToRect: rect, angle: angle)
            }
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didCropToImage:withRect:angle:))) {
            self.onDidCropToRect = {[weak self] image, rect, angle in
                delegate.cropViewController!((self)!, didCropToImage: image, withRect: rect, angle: angle)
            }
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didCropToCircularImage:withRect:angle:))) {
            self.onDidCropToCircleImage = {[weak self] image, rect, angle in
                delegate.cropViewController!((self)!, didCropToCircularImage: image, withRect: rect, angle: angle)
            }
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didFinishCancelled:))) {
            self.onDidFinishCancelled = { [weak self] finished in
                delegate.cropViewController!((self)!, didFinishCancelled: finished)
            }
        }
    }

All 4 comments

Hi @kcankaynak!

Uh-oh. You're right. That certainly smells like a memory leak. I'll have to try it out in the sample app when I get a chance. Thanks for letting me know!

I have the same issue, there is a retain cycle causing my UIViewController (the one that presents your crop VC) to not be released, it doesnt call the deinit method as @kcankaynak mentioned. I thought it was your croppingVC delegate not being WEAK, because you had your delegate property as

public var delegate: CropViewControllerDelegate? {
        didSet { self.setUpDelegateHandlers() }
    }

so I thought.. maybe

public weak var delegate: CropViewControllerDelegate? {
didSet { self.setUpDelegateHandlers() }
}

will fix it... but it didnt :(

okay.. I fixed it with the following
1- I made the delegate weak, because delegates should always be weak
2- I made the self calls inside the handlers to be [weak self]

In your CropViewController.swift see the onDid... callbacks.

fileprivate func setUpDelegateHandlers() {
        guard let delegate = self.delegate else {
            onDidCropToRect = nil
            onDidCropImageToRect = nil
            onDidCropToCircleImage = nil
            onDidFinishCancelled = nil
            return
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didCropImageToRect:angle:))) {
            self.onDidCropImageToRect = {[weak self] rect, angle in
                delegate.cropViewController!((self)!, didCropImageToRect: rect, angle: angle)
            }
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didCropToImage:withRect:angle:))) {
            self.onDidCropToRect = {[weak self] image, rect, angle in
                delegate.cropViewController!((self)!, didCropToImage: image, withRect: rect, angle: angle)
            }
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didCropToCircularImage:withRect:angle:))) {
            self.onDidCropToCircleImage = {[weak self] image, rect, angle in
                delegate.cropViewController!((self)!, didCropToCircularImage: image, withRect: rect, angle: angle)
            }
        }

        if delegate.responds(to: #selector(CropViewControllerDelegate.cropViewController(_:didFinishCancelled:))) {
            self.onDidFinishCancelled = { [weak self] finished in
                delegate.cropViewController!((self)!, didFinishCancelled: finished)
            }
        }
    }

This was merged in another PR. Thanks for that!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rnkyr picture rnkyr  ·  4Comments

muranobu picture muranobu  ·  4Comments

ClaesClaes picture ClaesClaes  ·  6Comments

erickva picture erickva  ·  4Comments

asadqazi picture asadqazi  ·  3Comments