dequeueReusableCellWithIdentifier:forIndexPath:VS dequeueReusableCellWithIdentifier:
dequeueReusableCellWithIdentifier:forIndexPath: VS dequeueReusableCellWithIdentifier:
我已阅读 this question 并认为我了解这两种方法之间的区别,直到我找到一个奇怪的例子:
设置tableview cell的样式为Basic,Storyboard中的Identifier为Cell,代码如下:
import UIKit
class TableViewController: UITableViewController {
var items: [String]!
override func viewDidLoad() {
super.viewDidLoad()
items = ["first", "second", "third"]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// either works fine
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! // let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
很简单,但是当我把tableView:cellForRowAtIndexPath:
方法分别改成1、2、3、4种情况时:
案例 1:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例二:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例 3:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例 4:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例 1、2(不起作用):
案例 3、4(工作正常):
怎么解释?我觉得换个角度理解这两种方法真的很有帮助,欢迎大家提出意见。
dequeueReusableCellWithIdentifier:
不给你保证:单元格可能是 nil
,所以你必须检查你的单元格是否是 nil
并正确处理它,否则你的应用程序会崩溃。
另一方面,dequeueReusableCellWithIdentifier:forIndexPath:
会为您检查(它总是 return 一个单元格)。
对于您的特定情况 (Swift),这意味着您可以使用 dequeueReusableCellWithIdentifier:forIndexPath:
安全地强制展开单元格,而您必须使用 if let
语法第二个.
示例代码(在Objective-C中,我没有使用Swift)
dequeueReusableCellWithIdentifier:forIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" atIndexPath:indexPath];
// Here we know the cell is not nil (....atIndexPath: ensures it)
cell.textLabel.text = items[indexPath.row];
return cell;
}
dequeueReusableCellWithIdentifier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
// You asked for a cell, but you don't know if it is nil or not
// In Swift, here the cell should be a conditional
// First, check if the cell is nil
if ( cell == nil ) {
// Cell is nil. To avoid crashes, we instantiate an actual cell
// With Swift conditional should be something similar
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
}
// Here you're sure the cell is not nil
// If condicional, you probably will write cell?.textLabel?.text = items[indexPath.row];
cell.textLabel.text = items[indexPath.row];
// Finally, you return the cell which you're 100% sure it's not nil
return cell;
}
在每种情况下,每行都有两个单元格出队。在情况 1 和情况 2 中,您首先调用 ("Cell", forIndexPath: indexPath)
版本。在这种情况下,table 视图以 每行两个单元格 结束,一个完全重叠并遮盖另一个。您可以在视图检查器中看到这一点,因为您可以修改视角以查看后面:
(我修改了 cellForRowAtIndexPath
代码,如下所示:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "First cell for row \(indexPath.row)"
cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "Second cell for row \(indexPath.row)"
print("Cell being returned is \(cell)")
return cell
}
为每个单元格提供不同的文本标签。)在情况 3 和 4 中,您首先调用 ("Cell")
版本,table 视图的每一行只有一个单元格。
为什么会有不同的行为?如果您创建 UITableViewCell
的自定义子类并在故事板中使用它,则您可以重写各种方法并添加 print()
语句以查看发生了什么。特别是 awakeFromNib
、didMoveToSuperView
和 deinit
。所发生的是,在情况 1 和情况 2 中,第一个单元格被创建 (awakeFromNib) 并立即添加 (didMoveToSuperView) 到超级视图,大概是 table 视图或其子视图之一。在情况 3 和 4 中,创建了第一个单元格但未将其添加到父视图。取而代之的是一段时间后,该单元被释放(deinit)。
(请注意,如果第二个单元格使用 ("Cell", forIndexPath: indexPath)
版本出队,它也会立即添加到超级视图。但是,如果第二个单元格使用 ("Cell")
版本出队,它仅在 cellForRowAtIndexPath
方法返回 后添加到超级视图 。)
所以关键区别在于 ("Cell", forIndexPath: indexPath)
版本导致单元格被立即添加到 table 视图,甚至在 cellForRowAtIndexPath
完成之前。这在您引用的 question/answer 中有所暗示,因为它表明出队单元格的大小将正确。
一旦添加到父视图,第一个单元格就不能被释放,因为它的父视图仍然对它有强引用。如果单元格使用 ("Cell")
版本出队,则它们不会添加到超级视图中,因此一旦 cell
变量被重新分配,它们就没有强引用,因此它们会被释放。
希望一切都有意义。
我已阅读 this question 并认为我了解这两种方法之间的区别,直到我找到一个奇怪的例子:
设置tableview cell的样式为Basic,Storyboard中的Identifier为Cell,代码如下:
import UIKit
class TableViewController: UITableViewController {
var items: [String]!
override func viewDidLoad() {
super.viewDidLoad()
items = ["first", "second", "third"]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// either works fine
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! // let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
很简单,但是当我把tableView:cellForRowAtIndexPath:
方法分别改成1、2、3、4种情况时:
案例 1:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例二:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例 3:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例 4:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例 1、2(不起作用):
案例 3、4(工作正常):
怎么解释?我觉得换个角度理解这两种方法真的很有帮助,欢迎大家提出意见。
dequeueReusableCellWithIdentifier:
不给你保证:单元格可能是 nil
,所以你必须检查你的单元格是否是 nil
并正确处理它,否则你的应用程序会崩溃。
dequeueReusableCellWithIdentifier:forIndexPath:
会为您检查(它总是 return 一个单元格)。
对于您的特定情况 (Swift),这意味着您可以使用 dequeueReusableCellWithIdentifier:forIndexPath:
安全地强制展开单元格,而您必须使用 if let
语法第二个.
示例代码(在Objective-C中,我没有使用Swift)
dequeueReusableCellWithIdentifier:forIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" atIndexPath:indexPath];
// Here we know the cell is not nil (....atIndexPath: ensures it)
cell.textLabel.text = items[indexPath.row];
return cell;
}
dequeueReusableCellWithIdentifier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
// You asked for a cell, but you don't know if it is nil or not
// In Swift, here the cell should be a conditional
// First, check if the cell is nil
if ( cell == nil ) {
// Cell is nil. To avoid crashes, we instantiate an actual cell
// With Swift conditional should be something similar
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
}
// Here you're sure the cell is not nil
// If condicional, you probably will write cell?.textLabel?.text = items[indexPath.row];
cell.textLabel.text = items[indexPath.row];
// Finally, you return the cell which you're 100% sure it's not nil
return cell;
}
在每种情况下,每行都有两个单元格出队。在情况 1 和情况 2 中,您首先调用 ("Cell", forIndexPath: indexPath)
版本。在这种情况下,table 视图以 每行两个单元格 结束,一个完全重叠并遮盖另一个。您可以在视图检查器中看到这一点,因为您可以修改视角以查看后面:
(我修改了 cellForRowAtIndexPath
代码,如下所示:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "First cell for row \(indexPath.row)"
cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "Second cell for row \(indexPath.row)"
print("Cell being returned is \(cell)")
return cell
}
为每个单元格提供不同的文本标签。)在情况 3 和 4 中,您首先调用 ("Cell")
版本,table 视图的每一行只有一个单元格。
为什么会有不同的行为?如果您创建 UITableViewCell
的自定义子类并在故事板中使用它,则您可以重写各种方法并添加 print()
语句以查看发生了什么。特别是 awakeFromNib
、didMoveToSuperView
和 deinit
。所发生的是,在情况 1 和情况 2 中,第一个单元格被创建 (awakeFromNib) 并立即添加 (didMoveToSuperView) 到超级视图,大概是 table 视图或其子视图之一。在情况 3 和 4 中,创建了第一个单元格但未将其添加到父视图。取而代之的是一段时间后,该单元被释放(deinit)。
(请注意,如果第二个单元格使用 ("Cell", forIndexPath: indexPath)
版本出队,它也会立即添加到超级视图。但是,如果第二个单元格使用 ("Cell")
版本出队,它仅在 cellForRowAtIndexPath
方法返回 后添加到超级视图 。)
所以关键区别在于 ("Cell", forIndexPath: indexPath)
版本导致单元格被立即添加到 table 视图,甚至在 cellForRowAtIndexPath
完成之前。这在您引用的 question/answer 中有所暗示,因为它表明出队单元格的大小将正确。
一旦添加到父视图,第一个单元格就不能被释放,因为它的父视图仍然对它有强引用。如果单元格使用 ("Cell")
版本出队,则它们不会添加到超级视图中,因此一旦 cell
变量被重新分配,它们就没有强引用,因此它们会被释放。
希望一切都有意义。