2016-03-14 2 views
6

Я прочитал this question и думаю, что я понимаю разницу между этими двумя методами, пока я не нашел странный пример:dequeueReusableCellWithIdentifier: forIndexPath: VS dequeueReusableCellWithIdentifier:

Set стиль вида таблицы ячейки быть Basic, идентификатор будет 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 
    } 
} 

enter image description here

Очень простой, но когда я изменить метод 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 
} 

Случай 2:

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 (не работает):

enter image description here

Случай 3, 4 (отлично работает):

enter image description here

Как объяснить? Я думаю, что это действительно помогает понять эти два метода с другой точки зрения, любое мнение приветствуется.

ответ

6

В в каждом случае вы re dequeueing две ячейки для каждой строки. В случаях 1 и 2 вы сначала вызываете ("Cell", forIndexPath: indexPath).В этом случае представление таблицы заканчивается двумя ячейками для каждой строки, одна полностью перекрывающая и затеняющая другую. Вы можете увидеть это в виде инспекторах, так как вы можете изменить угол зрения, чтобы увидеть позади:

enter image description here

(я внес изменения в 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"), представление таблицы имеет только одну ячейку для каждой строки.

Почему различное поведение? Если вы создаете собственный подкласс UITableViewCell и используете его в своем раскадровке, вы можете переопределить различные методы и добавить print() заявления, чтобы узнать, что происходит. В частности, awakeFromNib, didMoveToSuperView и deinit. Что происходит, так это то, что в случаях 1 и 2 первая ячейка создается (awakeFromNib) и сразу же добавляется (didMoveToSuperView) в супервизор, предположительно в виде таблицы или в одном из ее подзонов. В случаях 3 и 4 первая ячейка создается, но не добавляется в супервизор. Вместо этого через некоторое время ячейка освобождается (deinit).

(Обратите внимание, что если вторая ячейка из очереди с использованием версии ("Cell", forIndexPath: indexPath), он тоже сразу же добавляете к надтаблице. Однако, если вторая ячейка из очереди с использованием версии ("Cell"), он добавляется только к надтаблицам после cellForRowAtIndexPath метод вернулся.)

Таким образом, ключевым отличием является то, что ("Cell", forIndexPath: indexPath) версия приводит к клетке быть немедленно добавлены в виде таблицы, прежде, чем даже cellForRowAtIndexPath завершена. Это намекает в вопросе/ответе, на который вы ссылаетесь, поскольку это указывает на то, что выделенная ячейка будет иметь правильный размер.

После добавления в супервизор, первая ячейка не может быть освобождена, так как по-прежнему существует сильная ссылка на нее из ее супервизора. Если ячейки отменены с помощью версии ("Cell"), они не добавляются в супервизор, поэтому нет сильной ссылки на них после перераспределения переменной cell, и поэтому они освобождаются.

Надеюсь, все это имеет смысл.

+0

Ваш ответ потрясающий, спасибо кучу! – fujianjin6471

1

dequeueReusableCellWithIdentifier: не дает вам гарантий: ячейки могут быть nil, поэтому вам нужно проверить, находится ли ваша ячейка nil и обрабатывать ее должным образом или ваше приложение будет аварийно завершено.

dequeueReusableCellWithIdentifier:forIndexPath:, с другой стороны, проверяет это для вас (он всегда возвращает ячейку).

Для вашего конкретного случая (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; 
} 
+0

Почему нет случаев 1, 2 работает как ожидалось? – fujianjin6471

+0

Не уверен (мне не нравится Swift, поэтому я не слишком много копаю в нем), но похоже, что ваш атрибут 'textLabel' не является условным, а фактическим' UITextField', поэтому он пытается развернуть его ('?') будет терпеть неудачу, поскольку это фактический объект, а не оболочка. Вы можете видеть, что фактическая ячейка ** есть **, потому что в противном случае ваше приложение потерпит крах (возвращение 'nil' в' tableView: cellForRowAtIndexPath: 'вызывает исключение и вылетает при возврате' nil'). Попробуйте 'cell.textLabel.text' без'? 'Или смесь условной и силовой развертки ('? 'И'! '). –

+0

Спасибо, но я думаю, что имеет смысл не язык, который мы используем. @ pbasdf ответ просветил меня, это действительно помогает – fujianjin6471