Зачем мне когда-либо использовать незадействованное я?

В приложениях iOS часто наблюдается следующий шаблон:

class MyViewController: UIViewController {
    let myModel = MyModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        myModel.foo() { [***] in
            // use self here
        }
    }
}

class MyModel {
    public func foo(complete: () -> Void) {
        // do something
        complete()
    }
}

Консенсус заключается в том, чтобы вместо [***] или [weak self] использовать [***], не имеющий права, если вы можете гарантировать, что self не будет nil на момент завершения и слабым, если вы не уверены, что ссылка будут по-прежнему действительны. То, что я не понимаю, - это то, почему я когда-либо рискнул бы использовать unowned, возможно, я уверен, что сейчас ссылка никогда не будет нулевой, но это может измениться в будущем. Я мог бы также забыть о краю, ошибки случаются. Я мог бы так же легко всегда использовать слабый, и поставить охрану в верхней части крышки, чтобы иметь возможность использовать себя без! или?.
Что такое использование неработающих? Это быстрее, чем слабый + охранник? Это синтаксический сахар? Похоже, что это противоречит философии Свифта, защищающей разработчика от распространенных ошибок, которые могут вызвать сбои.

Ответ 1

unowned имеет предельное преимущество в производительности перед weak, потому что время выполнения не должно отслеживать ссылку, чтобы превратить ее в nil, когда объект уходит.

В отношении циклов удержания (ну, сильные ссылочные циклы) ни weak, ни unowned не создает сильную ссылку (в условиях до ARC не увеличивает счетчик удержания), поэтому нет опасности для эталонного цикла, на самом деле, поэтому вам нужно указать weak или unowned для self в закрытии.

Кроме того, с unowned вы можете использовать ссылку как необязательную, поэтому вам не нужно класть какой-либо код в закрытие, чтобы развернуть его.

Я всегда использую weak, если нет хорошей причины для производительности.

NB в вашем коде, я не думаю, что это необходимо, потому что закрытие не ускользает. Т.е. ссылка на него, полученная в вызове функции foo, не сохраняется после окончания области foo.

Ответ 2

Я считаю, что использование unowned добавляет к риску, чем использование слабого "я". Ошибки случаются, и один из примеров этого будет запускать вызов API в контроллере представления, позволять ему ждать ответа, и внезапное появление этого контроллера представления может привести к его освобождению (если нет сильных ссылок на него). К моменту поступления ответа наш объект контроллера наблюдения исчезнет, ​​и наше приложение рухнет на наше лицо. Нам решать, какой из них использовать в каком месте.

Как отметил Джереми, unowned не несет ответственности за отслеживание количества ссылок, поэтому у него всегда было небольшое преимущество в производительности по сравнению с сильным и слабым.

Ответ 3

По Руководство по программированию коммутатора: автоматический подсчет ссылок

Ожидается, что неизменная ссылка всегда будет иметь значение. В результате ARC никогда не устанавливает значение unowned reference в значение nil, а это означает, что неопубликованные ссылки определяются с использованием неопытных типов.

Короче говоря, weak являются опциями, где unowned нет.

Ответ 4

Простой пример использования неизвестной ссылки в контроллере навигации:

class RegisterViewController: UIViewController {
    lazy var navigatinRightBarItemsCount: Int = { [unowned self] in
        guard let navigationController = self.navigationController else { return 0 }
        guard let topItem = navigationController.navigationBar.topItem else { return 0 }
        guard let rightBarButtonItems = topItem.rightBarButtonItems else { return 0 }
        return rightBarButtonItems.count
        }()

    override func viewWillAppear(_ animated: Bool) {
        print("Navigation bar right side items count: ", navigationRightBarItemsCount)
    }
}

Это означает, что RegisterViewController должен быть инициализирован, чем мы можем безопасно получить самостоятельно. navigationController и отсюда, чтобы получить количество элементов. Если вы попытаетесь сделать это как [слабый self], XCode будет жаловаться, потому что self (viewcontroller) может быть нулевым, поэтому мы должны использовать unowned.

Еще один пример с UIBarButtonItem

lazy final private var filterNavigationBarItem: UIBarButtonItem = { [unowned self] in
    return UIBarButtonItem(image: #imageLiteral(resourceName: "filter_icon"),
                           style: .plain,
                           target: self,
                           action: #selector(segueToFilterJobs))
    }()

Что мы видим здесь? Посмотрите цель, которая обозначает "Объект, который получает сообщение действия". Это означает, что объект, который получает сообщение действия, не может быть nil и должен быть инициализирован.

Очень хорошее объяснение от drewag в этой статье.

Единственный раз, когда вы действительно хотите использовать [непризнанное я] или [слабое я], это когда вы создаете сильный ссылочный цикл. Сильный референсный цикл - это когда существует цикл владения, когда объекты в конечном итоге становятся собственниками друг друга (возможно, через третьих лиц), и поэтому они никогда не будут освобождены, потому что они оба гарантируют, что друг друга останутся.