user9832689
user9832689

Reputation: 33

Memory Leak in UIButton extension for fadeOut animation in swift4

I built an extension to the UIButton class to do fadeOut. When I use this I get memory leak warning in the profiler. I am using Swift 4 and Xcode 9.3.

Thanks in advance for any help.

extension UIButton {
    func fadeOut() { 
        let fadeOut = CABasicAnimation(keyPath: "opacity")
        fadeOut.duration = 0.35
        fadeOut.fromValue = 1
        fadeOut.toValue = 0.0
        fadeOut.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
        fadeOut.autoreverses = false
        fadeOut.repeatCount = 0
        fadeOut.isRemovedOnCompletion = true

        self.layer.add(fadeOut, forKey: nil)
    }
}

The calling function is given below. Also please note: new, level and card are UIButtons. When I comment out button.fadeout() in the function below the memory leak goes away as per the Xcode profiler. Hope this gives more context. If any other information is required to help analyze, I happy to provide the info.

private func menu_fadeout(){        
    func menu_fadeout_helper(_ button:UIButton){
        button.fadeOut()
        button.isHidden = true
        button.isEnabled = false
    }

    menu_fadeout_helper(hint)
    menu_fadeout_helper(new)
    menu_fadeout_helper(level)
    menu_fadeout_helper(card)
}

Upvotes: 1

Views: 290

Answers (3)

sergey mishunin
sergey mishunin

Reputation: 11

  • look for reference cycle (use weak/unowned self in capture lists)
  • remove all animations from layers
  • invalidate timer if there is one

Upvotes: 0

user9832689
user9832689

Reputation: 33

After lots of poking around it turns out the animation layers cause leaks for various reasons - most have guesses but no precise answers.

To solve my problem I reimplemented the fadeOut function without using the CABasicAnimation and using UIView.animate and made NO other change to the code. The profiler has no issues now - all is good. Thanks!

fyi there seems to inadvertent leaks whenever using stings in the context of buttons etc. If anyone has any pointers or suggestions on that topic would appreciate it.

Upvotes: 0

E. Huckabee
E. Huckabee

Reputation: 1818

After staring at the code for a couple minutes, I see the issue. In your function. . .

private func menu_fadeout(){        
    func menu_fadeout_helper(_ button:UIButton){
        button.fadeOut()
        button.isHidden = true
        button.isEnabled = false
    }

    menu_fadeout_helper(hint)
    menu_fadeout_helper(new)
    menu_fadeout_helper(level)
    menu_fadeout_helper(card)
}

. . .you never directly reference the UIButtons hint, new, level, and card. Eventually, after pressing the buttons a ton of times, the memory will fill up with nothing and your app will crash. (or worse)

Change the function to this to (supposedly) remove the memory leak.

private func menu_fadeout(){        
    func menu_fadeout_helper(_ button: UIButton) -> UIButton {
        button.fadeOut()
        button.isHidden = true
        button.isEnabled = false

        return button
    }

    menu_fadeout_helper(self.hint)
    menu_fadeout_helper(self.new)
    menu_fadeout_helper(self.level)
    menu_fadeout_helper(self.card)
}

Upvotes: 0

Related Questions