【问题标题】:Why use "weak" keyword in Swift when declaring Views in a ViewController for iOS为什么在 iOS 的 ViewController 中声明视图时在 Swift 中使用“weak”关键字
【发布时间】:2019-12-05 18:49:55
【问题描述】:

以下在 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)
    }
}

【问题讨论】:

  • 唯一一次我会在强制展开时使用弱的情况是,如果你的界面是内置在 xib/storyboard 中的。 xib 将使出口保持活动状态,因此视图控制器不需要保留它们。在几乎任何其他情况下,我都会将视图声明为强视图,而不是强制展开它们。
  • @Dare 底部的两个怎么样,它们是在声明中构造的?为什么不这样做,而不是在 viewDidLoad 期间初始化它们?
  • 声明和初始化一个弱变量以供稍后在 xib 不保留时使用是不正确的。没有什么可以保留它,因此它可以随机/立即解除分配。出于这个原因,我永远不会使用最后一个示例。倒数第二个例子是可以的,但一般来说,你需要对视图做更多的事情而不是简单地初始化它,所以你真的应该将该逻辑封装在一个函数中。是否显式打开它取决于它的初始化位置和您遵循的样式指南。

标签: ios swift xcode uiview uikit


【解决方案1】:

您的问题可能与 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)
*/

【讨论】:

    猜你喜欢
    • 2013-03-11
    • 2019-12-26
    • 2010-12-04
    • 2016-12-19
    • 2016-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    相关资源
    最近更新 更多