UIViewController 中的集合视图可选展开崩溃
Posted
技术标签:
【中文标题】UIViewController 中的集合视图可选展开崩溃【英文标题】:Collection View in UIViewController optional unwrapping crash 【发布时间】:2017-02-16 10:51:05 【问题描述】:对于 Swift 来说还是很陌生,并且已经阅读/了解了“最佳实践”,我正在尝试重构一些简单的代码,但无法将我的头脑围绕可选选项并将简单的 UICollectionView
放在 UIViewController
中。
我有什么,什么有效
class AddFriendsController: UIViewController
fileprivate let cellId = "cellId"
override func viewDidLoad()
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(handleCancel))
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 111, height: 111)
let collectionViewTest = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionViewTest.delegate = self
collectionViewTest.dataSource = self
collectionViewTest.register(UserFriendCell.self, forCellWithReuseIdentifier: cellId)
view.addSubview(collectionViewTest)
collectionViewTest.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
collectionViewTest.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
collectionViewTest.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
collectionViewTest.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
@objc private func handleCancel()
self.dismiss(animated: true, completion: nil)
extension AddFriendsController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
func numberOfSections(in collectionView: UICollectionView) -> Int
return 2
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 9
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! UserFriendCell
return cell
我希望它是什么样的
class AddFriendsController: UIViewController
fileprivate let cellId = "cellId"
private weak var collectionViewTest: UICollectionView?
private weak var layout: UICollectionViewFlowLayout?
override func viewDidLoad()
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(handleCancel))
layout = UICollectionViewFlowLayout()
layout!.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout!.itemSize = CGSize(width: 111, height: 111)
collectionViewTest = UICollectionView(frame: self.view.frame, collectionViewLayout: layout!)
collectionViewTest!.delegate = self
collectionViewTest!.dataSource = self
collectionViewTest!.register(UserFriendCell.self, forCellWithReuseIdentifier: cellId)
view.addSubview(collectionViewTest!)
collectionViewTest!.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
collectionViewTest!.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
collectionViewTest!.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
collectionViewTest!.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
@objc private func handleCancel()
self.dismiss(animated: true, completion: nil)
extension AddFriendsController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
func numberOfSections(in collectionView: UICollectionView) -> Int
return 2
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 9
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! UserFriendCell
return cell
这样做是因为我想在学习 Swift 时从一开始就尝试应用“最佳实践”,即根据我在这种情况下的理解,尽可能多地制作 private
和 weak
ify/"optionalize" 视图/outlets 以避免保留循环。
问题
由于 bang 在 layout!.sectionInset
级别展开 nil,应用程序崩溃。
哪位好心人愿意开导我?
提前致谢。
【问题讨论】:
为什么你的布局属性很弱?它是否保留了视图控制器?为什么不从声明中初始化它?所以你不需要强制解包:private let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
【参考方案1】:
看起来你在声明你的布局很弱。如果您将其更改为强,即只需删除弱,那么您应该没问题。
您的 CollectionView 也是如此。
同样作为风格问题,您可以将布局和 collectionView 声明为隐式展开,而不是每次都展开。
比如private var layout: UICollectionViewFlowLayout!
【讨论】:
同时从collectionView中移除weak,你应该在制作IBOutlet的时候使用weak,因为这个view是被创建并保存在storyboard/xib中的! 工作正常@Chris,谢谢! @Vadim 好的,这是我的主要审问:weak
ify 视图实际上仅在从情节提要设计 UI 时才需要,但由于您刚才引用的原因而不是编程方式,即在我的情况下,当没有其他指向它时,它将被安全地释放?如果我在另一个控制器 controllerB
中有对 collectionViewTest
的引用怎么办:我是否应该在 weak
ify 这个引用在 controllerB
中避免在我解除 controllerB
时出现保留循环?【参考方案2】:
你的代码有问题。
如果您将变量作为可选变量,例如
private weak var layout: UICollectionViewFlowLayout?
然后在强制解包之前,您应该确认这是非零。所以在这一行它会崩溃,因为你试图强行解开一个实际上是 nil 的变量。
layout!.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
所以如果你想在不添加非零检查的情况下做喜欢它,那么你应该写。
layout?.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
但是这里你的代码也是错误的,因为你不应该使用变量weak,因为弱引用只是一个指向对象的指针,它不能保护对象不被ARC释放。
这里你希望 UICollectionViewFlowLayout 和 UICollectionView 贯穿 UIViewController 的生命周期,所以你不应该认为它很弱。
所以你应该将两者都定义为下。
private var collectionViewTest: UICollectionView!
private var layout: UICollectionViewFlowLayout!
或
private var collectionViewTest: UICollectionView?
private var layout: UICollectionViewFlowLayout?
【讨论】:
感谢@Nikunj 提供的详细信息 这是我的荣幸 :)以上是关于UIViewController 中的集合视图可选展开崩溃的主要内容,如果未能解决你的问题,请参考以下文章
一个 UIViewController 中的两个 UICollectionView
如何在 Swift 的 UIViewController 中添加多个集合视图?