Emre Önder
Emre Önder

Reputation: 2537

ViewController Doesn't Deinited

I have a FakeSplashController which does;

  1. ) Do a network request and wait for Its Result
  2. ) Show an animation then open SeconViewController

Something blocks this ViewController to be deallocated and deinit function doesn't get called.

In addition, AppInitService has a static func and It is called inside this SplashController. I also try to add [weak self] to network request. However, It also doesn't solve the problem.

class SplashViewController: UIViewController {

let logoImage:  UIImageView = {
    let imageView = UIImageView()
    imageView.image = UIImage(named: "logo")
    imageView.contentMode = .scaleAspectFit

    return imageView
}()

let textLogo: UIImageView = {
    let imageView = UIImageView()
    imageView.image = UIImage(named: "text-logo")
    imageView.contentMode = .scaleAspectFit
    return imageView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    setupUI()
    networkRequests()

}

func networkRequests(){

    AppInitService().initAppRequest { [](result) in
        switch result{
        case .success(_):
            self.startAnimation()
            return
        case .error(let error):
            UIControlUtil.showErrorMessage(title: error.title, message: error.message, closeButton: true)
            return
        }
    }
}

func openApp(){
    let loginController = WelcomeViewController()
    guard let window = UIApplication.shared.keyWindow else {
        return
    }
    window.rootViewController = loginController
}

func startAnimation(){
    UIView.animate(withDuration: 0.8, animations: {
        self.logoImage.frame.origin.x -= 100
    }, completion: nil)
    UIView.animate(withDuration: 1,delay: 0.3,animations: {
        self.textLogo.alpha = 1
        self.textLogo.frame.origin.x += 50
    }, completion: { _ in
        self.openApp()
    })
}

deinit {
    print("Splash Deinited")
}
func setupUI(){
    self.view.backgroundColor = Color.NavigationBar.tintColor
    logoImage.frame = CGRect(x: 0, y: 0, width: 80, height: 80)
    logoImage.center = self.view.center
    self.view.addSubview(logoImage)
    textLogo.frame = CGRect(x: 0,y: 0, width: 195, height: 80)
    textLogo.center = self.view.center
    textLogo.frame.origin.x -= 20
    self.view.addSubview(textLogo)
    textLogo.alpha = 0
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
}

Upvotes: 0

Views: 269

Answers (4)

Virajkumar Patel
Virajkumar Patel

Reputation: 1562

Without using weak reference function cannot release self object so this function generate retain cycle instants of you can use weak reference.

// You can use weak reference certain to not capture self by updating it to...
func networkRequests(){
    AppInitService().initAppRequest { [weak self] result in
        guard let strongSelf = self else {
            return
        }
        switch result{
        case .success(_):
            strongSelf.startAnimation() // HERE BE DRAGONS!
            return
        case .error(let error):
            UIControlUtil.showErrorMessage(title: error.title, message: error.message, closeButton: true)
            return
        }
    }
}

Upvotes: 0

Fogmeister
Fogmeister

Reputation: 77661

You are capturing self in this block...

func networkRequests(){    
    AppInitService().initAppRequest { result in
        switch result{
        case .success(_):
            self.startAnimation() // HERE BE DRAGONS!
            return
        case .error(let error):
            UIControlUtil.showErrorMessage(title: error.title, message: error.message, closeButton: true)
            return
        }
    }
}

This is a potential cause of your memory leak.

You can be certain to not capture self by updating it to...

func networkRequests(){
    AppInitService().initAppRequest { [weak self] (result) in
        switch result{
        case .success(_):
            self?.startAnimation()
            return
        case .error(let error):
            UIControlUtil.showErrorMessage(title: error.title, message: error.message, closeButton: true)
            return
        }
    }
}

Upvotes: 1

Emre Önder
Emre Önder

Reputation: 2537

The problem was I was creating this SplashController as a global and optional variable inside AppDelegate as;

var splashController: SplashController?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        splashController = SplashController()
        self.window?.rootViewController = splashController

        return true
    }

I make it as:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let splashController = SplasController()
        self.window?.rootViewController = splashController
        return true
    }

Upvotes: 0

aBilal17
aBilal17

Reputation: 3152

You can first get all the view controllers in the array and then after checking with the corresponding view controller class, you can delete the one you want.

Here is small piece of code:

var navArray:Array = (self.navigationController?.viewControllers)!
navArray.removeAtIndex(0)
self.navigationController?.viewControllers = navArray

I think this will make your work easier.

Upvotes: 0

Related Questions