Xcode 10 - экземпляр будет немедленно освобожден, поскольку свойство "слабое"

Недавно я загрузил Xcode 10, и я заметил явную ошибку при использовании weak или unowned переменных. Мне удалось создать простой пример, демонстрирующий проблему, чтобы люди могли ее воссоздать.

class MainClass {
    weak var weakClass: SomeClass!

    init() {

        // WARNING: Instance will be immediately deallocated because property 'weakClass' is 'weak'

        self.weakClass = SomeClass()
    }
}

class SomeClass {}

Как говорится в ошибке, weakClass немедленно освобождается после того, как MainClass инициализирован и всегда равен нулю.

Я открыл ту же площадку с Xcode 9.3, и я могу подтвердить, что код работает нормально, без ошибок или предупреждений

Это ошибка в Xcode 10, или я ничего не получаю. Если это так, есть ли какие-нибудь обходные пути?

EDIT: оригинальный пример

class LoginCoordinator {

    var viewModel: LoginViewModel?
    var viewController: LoginViewController?

    init() {
        viewModel = LoginViewModel()
        viewModel?.coordinator = self
        viewController = LoginViewController(viewModel: viewModel!)
    }
}


class LoginViewModel: ViewModelDelegate {
    weak var coordinator: LoginCoordinator?
}

coordinator всегда ноль в LoginViewModel

AppDelegate.swift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func setupView() {
        let coordinator = LoginCoordinator()
        let navigationController = UINavigationController(rootViewController: coordinator.create)

        navigationController.isNavigationBarHidden = true
        navigationController.navigationBar.isTranslucent = false

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()
        window?.layer.cornerRadius = 6
        window?.layer.masksToBounds = true
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        setupView()
        return true
    }

Ответ 1

Чтобы понять это, вы должны знать концепцию ARC. Концепция ARC - это автоматический счетчик ссылок. ARC будет хранить что-то в памяти, если выделенная память сильно привязана некоторой переменной. Если он (ARC) обнаружил, что некоторая выделенная память не имеет какой-либо сильной ссылки, она отключит ее. Поэтому предупреждение weakClass немедленно deallocates после того, как MainClass инициализирован и всегда равен нулю. Потому что у него нет сильной ссылки. Пожалуйста, прокомментируйте любые сомнения.

Один пример ниже для создания цикла сохранения:

class A {
var classBObject: B?

  init() {
     classBObject = B()
     classBObject.classAObject = self // Creates a retain cycle
 }
}

class B {
   var classAObject: A? // Strong(by default all are strong) variable create retain cycle
}

Итак, в class B если мы возьмем weak var classAObject сохранение цикла не произойдет.

Ответ 2

Это и есть цель weak. Swift использует счетчик ссылок для управления памятью. Сильный указатель увеличивает счетчик ссылок остроконечного объекта на 1, слабый указатель не увеличивает счетчик ссылок. Объект с 0 счетчиком ссылок будет освобожден.

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

Слабый полезен, чтобы избежать удержаний циклов. Например, при экранировании закрытия и в шаблоне проектирования делегирования.

Ответ 3

Вопрос в том, "является ли эта ссылка сильной ссылкой в другом месте? Если это так, она не будет освобождена".

Я предлагаю, чтобы предупреждающее сообщение Apple вводило в заблуждение. Я думаю, что в нем должно быть указано, что он будет освобожден немедленно, когда его содержащий объект будет освобожден или когда будут удалены другие сильные ссылки на него.

Вот почему.

У нас есть это предупреждение на экземпляре в контроллере представления, и слабая переменная не освобождается немедленно. Появляется контроллер представления, создается слабая переменная, мы ждем, нажимаем кнопку, которая достигает точки останова, и да, слабая переменная все еще не равна нулю. Тем не менее, когда контроллер представления исчезает и освобождается, слабый var освобождается немедленно.

Но почему? Что ж, к тому времени, когда мы перейдем к той части кода, которая имеет слабую ссылку на переменную, другой код уже имеет счетчик сохранения 3, и хотя он слабый, он can't будет немедленно отклонен.

Вы можете проверить это с помощью po myObject.retainCount. Точность не гарантируется, но это даст вам представление. Если объект retaincount> 1 и он сильно связан где-то еще (пожалуйста, добавьте комментарий в свой код, чтобы указать, где на него ссылаются), будет работать слабый объект. Чтобы избежать предупреждения компилятора, не следует ссылаться непосредственно на объект, но на сильную ссылку в другом объекте.

Итак, я думаю, что Apple нужно перефразировать это предупреждение, потому что оно, безусловно, вводит в заблуждение.