Информационный портал по безопасности » Операционные системы » iOS » Нужно ли писать weak self в Grand Central Dispatch?

 

Нужно ли писать weak self в Grand Central Dispatch?

Автор: admin от 19-05-2019, 19:35, посмотрело: 85

Тут у нас возник спор: нужно ли писать [weak self] в GCD?



Один говорит:

– [weak self] нужно писать везде!

Второй говорит:

– Нет, даже если не писать [weak self] внутри DispatchQueue, утечки памяти не будет.



Вместо того, чтобы разбираться, легче написать пару строк. Тяжелее об этом написать пост.

Итак, мы создадим UIViewController, в котором будет вызываться метод в DispatchQueue через пять секунд после viewDidLoad.



class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad")
        
        DispatchQueue.main.asyncAfter(deadline: .now()+5, execute: { [weak self] in
            self?.method()
        })
    }
    
    func method() {
        print("method")
    }
    
    deinit {
        print("deinit")
    }
    
}

Этот ViewController будет пушиться с другого ViewController-а. А сама суть в том, что за эти пять секунд, до вызова нашего метода, мы должны удалить этот ViewController со стэка UINavigationController. То есть, просто нажать назад.

После запуска в консоли видим:
viewDidLoad
deinit

То есть, после создания нашего ViewController-a вызвался viewDidLoad. Затем, после нажатия назад, наш ViewController удалился из памяти и вызвался deinit. А наш метод в DispatchQueue не вызвался с 19-ой строки, потому что в этот момент нашего ViewController-a уже не существует, self равно nil.

Теперь посмотрим, что будет если мы уберем [weak self] из DispatchQueue и оставим его так.

class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad")
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: {
            self.method()
        })
    }
    
    func method() {
        print("method")
    }
    
    deinit {
        print("deinit")
    }
    
}

Консоль:
viewDidLoad
method
deinit

Вызывается viewDidLoad. После пяти секунд исполняется наш метод и только потом ViewController деинитится. То есть, после нажатия назад, наш ViewController живет, пока не исполнится метод и только потом освобождается. Но утечки памяти не происходит! Потому что в итоге он удалился.

А что будет, если в DispatchQueue передать какой-нибудь closure. Вот так:

class SecondViewController: UIViewController {
    
    var closure: (()  Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad")
        
        closure = {
            print("closure")
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: closure!)
    }
    
    func method() {
        print("method")
    }
    
    deinit {
        print("deinit")
    }
    
}

Output:
viewDidLoad
deinit
closure

Вызывается viewDidLoad. Затем удаляется ViewController. А после пяти секунд исполняется наш closure. То есть ему не важно жив ViewController или нет. У него нету ссылки на наш ViewController. Он по-любому вызовется.

А как должно быть, чтобы произошла утечка? Нужно, чтобы наш closure вызывал метод ViewController-a, то есть имел на него ссылку.

class SecondViewController: UIViewController {
    
    var closure: (()  Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad")
        
        closure = {
            self.method()
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: closure!)
    }
    
    func method() {
        print("method")
    }
    
    deinit {
        print("deinit")
    }
    
}

В консоли:
viewDidLoad
method

Вот, в итоге deinit не вызвался и мы получили memory leak. А чтобы избавиться от него, нужно всего лишь в closure написать [weak self].

class SecondViewController: UIViewController {
    
    var closure: (()  Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad")
        
        closure = { [weak self] in
            self?.method()
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: closure!)
    }
    
    func method() {
        print("method")
    }
    
    deinit {
        print("deinit")
    }
    
}


Консоль:

viewDidLoad

deinit


Итог



Не важно, писать [weak self] в GCD или нет, утечки памяти не будет. Но надо знать, что у них поведение разное. В первом случае, то, что внутри Dispatch-a не исполнится. А во втором — исполнится, но до его исполнения ViewController будет жить.

Источник: Хабр / Интересные публикации

Теги: swift ios

Категория: iOS

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

Добавление комментария

Имя:*
E-Mail:
Комментарий:
  • bowtiesmilelaughingblushsmileyrelaxedsmirk
    heart_eyeskissing_heartkissing_closed_eyesflushedrelievedsatisfiedgrin
    winkstuck_out_tongue_winking_eyestuck_out_tongue_closed_eyesgrinningkissingstuck_out_tonguesleeping
    worriedfrowninganguishedopen_mouthgrimacingconfusedhushed
    expressionlessunamusedsweat_smilesweatdisappointed_relievedwearypensive
    disappointedconfoundedfearfulcold_sweatperseverecrysob
    joyastonishedscreamtired_faceangryragetriumph
    sleepyyummasksunglassesdizzy_faceimpsmiling_imp
    neutral_faceno_mouthinnocent