簡體   English   中英

為什么在 iOS 的 ViewController 中聲明視圖時在 Swift 中使用“weak”關鍵字

[英]Why use "weak" keyword in Swift when declaring Views in a ViewController for iOS

以下在 ViewController 中設置視圖的方法有什么區別

force-unwrapped

class VC: UIViewController() {
    var customView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        customView = UIView(frame: .zero)
        addSubview(customView)
    }
}

weak force-unwrapped

class VC: UIViewController() {
    weak var customView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        customView = UIView(frame: .zero)
        addSubview(customView)
    }
}

weak

class VC: UIViewController() {
    weak var customView: UIView

    override func viewDidLoad() {
        super.viewDidLoad()
        customView = UIView(frame: .zero)
        addSubview(customView)
    }
}

在聲明中構造:

class VC: UIViewController() {
    var customView = UIView(frame: .zero)

    override func viewDidLoad() {
        super.viewDidLoad()
        addSubview(customView)
    }
}

聲明中的weak構造

class VC: UIViewController() {
    weak var customView = UIView(frame: .zero))

    override func viewDidLoad() {
        super.viewDidLoad()
        addSubview(customView)
    }
}

可能是您的問題與 Xcode 創建網點有關:

@IBoutlet weak var label: UILabel!

Apple(和其他專家)已經討論過是否應該在這里使用弱(因為這些視圖幾乎總是由view本身保留 - 我敢肯定,如果你四處挖掘,你會發現關於它的無休止的討論。你通常會刪除weak上面沒有副作用(嘗試並查看 - 在視圖控制器dealloc添加日志消息。

但是,讓我們來談談您的具體問題。 我在一個測試項目中做了一個小班(每個人想要探索這種話題時都應該這樣做!):

final class MemoryTest: NSObject {

    lazy var vc1 = VC1()
    lazy var vc2 = VC2()
    lazy var vc4 = VC4()
    lazy var vc5 = VC5()

    func test() {
        let _ = vc1
        let _ = vc2
//        let _ = vc3
        let _ = vc4
        let _ = vc5
    }
}

// What is the difference between the following methods of setting up views in a ViewController

// `force-unwrapped`:

/*
    Interoperability with Objective C required this.
    Any code accessing vc1 before its set will crash, forced unwrapped means that Swift will always try to unwrap what is
    otherwise an optional, and if nil boom down youg go.

    Since Objective C can deal with nil objects, you can access it and read properties, all of which will be nil
 */
    @objcMembers
    class VC1: UIViewController {
        var customView: UIView!

        init() {
            super.init(nibName: nil, bundle: nil)

            //print("BG Color 1", customView.backgroundColor)   // Compiler warning then a crash
            print("BG Color 2", customView?.backgroundColor ?? "none")   // crash
            customView = UIView(frame: .zero)
            view.addSubview(customView)
            customView = nil // the view is still there - its retained by your view, but your reference to it is gone
            //print("BG Color 3", customView.backgroundColor ?? "none")   // crash
        }
        required init?(coder: NSCoder) { // compiler forces this
            fatalError("init(coder:) has not been implemented")
        }
    }

//`weak force-unwrapped`:

    @objcMembers
    class VC2: UIViewController {
        weak var customView: UIView!

        init() {
            super.init(nibName: nil, bundle: nil)

            // customView = UIView(frame: .zero)   // Compiler error - cannot even build - compiler knows this will be released immediately
            view.addSubview(UIView(frame: .zero))   // view retains this newly added view
            customView = view.subviews.first        // since its retained already, we can reference it "weakly" (meaning its not further retained)

            customView.backgroundColor = .yellow
            print("Frame", customView.frame)
            customView.removeFromSuperview()        // since customView is weak, it got nilled with the view was released

            // immediate refernces still work - there is some delay until that customView actually gets dealloed
            print("CustomerView", String(format: "Pointer %p", customView)) // Still there ...

            DispatchQueue.main.async {
                if self.customView == .none {
                    print("Its gone now!")
                }
                //print("CustomerView", String(format: "Pointer %p", self.customView)) // Crash!
            }
        }
        required init?(coder: NSCoder) { // compiler forces this
            fatalError("init(coder:) has not been implemented")
        }
    }

//`weak`: this is an illegal syntax and you cannot compile it

//    class VC3: UIViewController {
//        weak var customView: UIView     // Compiler hard error, requires either a "!" or "?" suffix
//
//        init() {
//            super.init(nibName: nil, bundle: nil)
//
//            customView = UIView(frame: .zero)
//            view.addSubview(customView)
//        }
//        required init?(coder: NSCoder) { // compiler forces this
//            fatalError("init(coder:) has not been implemented")
//        }
//    }

//constructed in declaration:

    @objcMembers
    class VC4: UIViewController {
        var customView = UIView(frame: .zero)

        init() {
            super.init(nibName: nil, bundle: nil)

            print("Frame", customView.frame)    // fine - the view is constructed immediately, and since its "strong" the view persists until
                                                // dealloc.
            super.viewDidLoad()
            view.addSubview(customView)
        }
        required init?(coder: NSCoder) { // compiler forces this
            fatalError("init(coder:) has not been implemented")
        }
    }

//`weak` constructed in declaration

    @objcMembers
    class VC5: UIViewController {
        weak var customView = UIView(frame: .zero)  // Compiler warning, the view is instantiated, then immediately released.
        { didSet { if customView == nil { print("NIL !!!") } } }  // does not work - the change isn't ob

        init() {
            print("CUSTOMVIEW", customView?.frame ?? "already gone") // its gone

            super.init(nibName: nil, bundle: nil)

            print("CUSTOMVIEW", customView?.frame ?? "already gone") // its gone

            // At this point, customView was allocated, then immediately released as its not retained elsewhere
            super.viewDidLoad()
            //view.addSubview(customView!)    // Crash
        }
        required init?(coder: NSCoder) { // compiler forces this
            fatalError("init(coder:) has not been implemented")
        }
    }

但是,Objective C 並不關心這些。 這是引用所有這些類的另一個類:

@implementation ObjcTest

- (void)test {
    VC1 *vc1 = [VC1 new];
    NSLog(@"vc1 %@", vc1.customView);

    VC2 *vc2 = [VC2 new];
    NSLog(@"vc2 %@", vc2.customView);

    VC4 *vc4 = [VC4 new];
    NSLog(@"vc4 %@", vc4.customView);

    VC5 *vc5 = [VC5 new];
    NSLog(@"vc5 %@", vc5.customView);
}

@end

/* Console:

 2019-12-05 16:37:52.534537-0500 TableSelections[51189:30629412] vc1 (null)
 2019-12-05 16:37:52.537241-0500 TableSelections[51189:30629412] vc2 <UIView: 0x7fcc73c34230; frame = (0 0; 0 0);
    layer = <CALayer: 0x600000001140>>
 2019-12-05 16:37:52.537535-0500 TableSelections[51189:30629412] vc4 <UIView: 0x7fcc760019e0; frame = (0 0; 0 0);
    layer = <CALayer: 0x6000000244a0>>
 2019-12-05 16:37:52.537734-0500 TableSelections[51189:30629412] vc5 (null)
*/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM