最近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
が初期化され、常にnilになると、MainClass
はすぐに割り当てを解除します。
Xcode 9.3で同じプレイグラウンドを開いたところ、エラーや警告なしでコードが正常に動作することを確認できます。
これはXcode 10のバグですか、それとも何かを得られませんか?存在する場合、回避策はありますか?
編集:オリジナルの例
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
で常にnilです
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
}
これを理解するには、ARC
の概念を知っている必要があります。 ARC
概念は自動参照カウントを意味します。ARC
は、割り当てられたメモリが何らかの変数によって強く参照されている限り、メモリに何かを保持します。 it(ARC)が割り当てられたメモリに強い参照を持たないことを発見した場合、それは割り当てを解除します。したがって、警告weakClass
すぐにdeallocates
一度MainClass
が初期化され、常にnilです。強力なリファレンスがないため、疑問点をコメントしてください。
保持サイクル作成の以下の1つの例:
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
保持サイクルは発生しません。
これがweak
の目的です。 Swiftはメモリを管理するために参照カウントを使用します。強いポインタはポイントされたオブジェクトの参照カウントを1増やします。弱いポインタは参照カウントを増やしません。参照カウントが0のオブジェクトは割り当て解除されます。
SomeClass
のインスタンスは弱いポインターによってのみポイントされているため、その参照カウントは0です。その結果、すぐに割り当てが解除されます。
弱いは、保持サイクルを回避するのに役立ちます。たとえば、クロージャのエスケープや委任デザインパターンなどです。
問題は、「その参照は他の場所で強く参照されていますか?その場合、割り当ては解除されません」です。
Appleの警告メッセージは誤解を招くものだと提案します。含まれているオブジェクトの割り当てが解除されたとき、またはそのオブジェクトへの他の強い参照が割り当て解除されたときに、すぐに割り当てが解除されると述べるべきだと思います。
その理由は次のとおりです。
View Controllerのインスタンスでこの警告が表示され、弱い変数はすぐに割り当て解除されません。 View Controllerが表示され、弱い変数がインスタンス化され、待機し、ブレークポイントとyupにヒットするボタンをクリックします。弱い変数はまだnilではありません。しかし、View Controllerが消えて割り当てが解除されると、弱いvar isが直ちに割り当て解除されます。
しかし、なぜ?さて、変数への弱い参照を持つコードの一部が登場する頃には、他のコードには既に3の保持カウントがあり、たとえ弱いとしてもcan't
はすぐに破棄されます。
これはpo myObject.retainCount
で確認できます。 notは正確であることが保証されていますが、アイデアを与えてくれます。オブジェクトのretaincountが1を超えており、他のどこかに強くリンクされている場合(コードにコメントを入れて、強く参照されている場所を示してください)、weakは機能します。コンパイラの警告を回避するには、オブジェクトを直接参照せず、他のオブジェクトの強参照を参照してください。
だから、Appleは間違いを招くのでこの警告を言い換える必要があると思う。