1

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)
}
E. Huckabee
  • 1,788
  • 1
  • 13
  • 29
  • How are we supposed to help you when we have no idea what you did? Memory leaks can be caused by a myriad of different things. You literally just told us your problem, gave us a random function, and then told us to solve your problem for you. (Thats not what SO is about. Its about helping people solve their problems) I have no idea what is causing your leak, and I won't be able to figure it out without more information. – E. Huckabee May 23 '18 at 07:24
  • I am quite sure the code you posted will not cause a memory leak on its own. – Au Ris May 23 '18 at 07:33
  • I admit that I am a first time poster so may not know how to post the full question. When I ran the profiler it said the leak is caused when I call this function. And when I don't call this function the memory leak disappears from the profiler. That is why I posted this part fo the code. Does that help? – user9832689 May 23 '18 at 07:40
  • Incredibly helpful, thanks. When you call the function does the app crash? Do you get an error message? If so, what does the error say? – E. Huckabee May 23 '18 at 07:56
  • On the simulator and the app on phone runs fine for the stretch of time that I have used it - does not crash and does not give error messages. I was working with profiler to see if my app had leaks before trying to make a release for my friends to test. In doing so I discovered what I mentioned above. – user9832689 May 23 '18 at 07:59
  • The code above cannot cause any memory leaks. Have you tried leaving the app running for a couple hours? – E. Huckabee May 23 '18 at 08:03
  • I have been reading about it on other forums and blogs. There is commentary about how self in closure creates a memory cycle and using the "weak" or "unowned" might be a solution. But not clear to me if and how those strategies apply in this context. If the language is call by value and does a decent job of reference counting it should sort this stuff out. – user9832689 May 23 '18 at 08:03
  • I have let it run for about an hour with no issues. I will let it run fo r the next 4-5 hours and see.. – user9832689 May 23 '18 at 08:04
  • Could you give me a link to some of the blogs/forums? – E. Huckabee May 23 '18 at 08:06
  • Let me put a few. They say more or less the same stuff. https://medium.com/@sergueivinnitskii/most-common-memory-leak-trap-in-swift-4565dbae5445 – user9832689 May 23 '18 at 08:09
  • Another link. https://medium.com/flawless-app-stories/memory-leaks-in-swift-bfd5f95f3a74 – user9832689 May 23 '18 at 08:13
  • Add `self.button.fadeOut` instead of `button.fadeOut` – E. Huckabee May 23 '18 at 08:17
  • self does not have button... it has the hint, new, level and card as UIButtons.... When I put self it says UIViewController does not have self. – user9832689 May 23 '18 at 08:22

3 Answers3

0

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)
}
E. Huckabee
  • 1,788
  • 1
  • 13
  • 29
  • Going to try ti today and will post feedback. Ty @E.Huckabee for your suggestions! – user9832689 May 23 '18 at 13:09
  • Tried it in every way possible including the way suggested no impact. Observations (1) I can have the way the code is and not call fadeOut and all is fine. (2) Unless I comment out the creation of the Animation Layer in the fadeOut the memory leak will persist no matter what. Will provide more info as I work on it... – user9832689 May 23 '18 at 14:14
0

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.

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