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 нужно перефразировать это предупреждение, потому что оно, безусловно, вводит в заблуждение.