2016-03-14 20 views
6

Tôi đã đọc this question và nghĩ rằng tôi hiểu sự khác biệt giữa hai phương pháp cho đến khi tôi tìm thấy một ví dụ lạ:dequeueReusableCellWithIdentifier: forIndexPath: VS dequeueReusableCellWithIdentifier:

Set phong cách xem bảng tế bào của thể Basic, định danh được di động trong Storyboard, mã như sau:

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

Rất đơn giản, nhưng khi tôi thay đổi phương pháp tableView:cellForRowAtIndexPath:-1, 2, 3, 4 trường hợp tương ứng:

Trường hợp 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 
} 

Trường hợp 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 
} 

Trường hợp 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 
} 

Trường hợp 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 
} 

Trường hợp 1, 2 (không hoạt động):

enter image description here

Trường hợp 3, 4 (chỉ hoạt động tốt):

enter image description here

Làm thế nào để giải thích? Tôi nghĩ rằng nó thực sự giúp hiểu hai phương pháp này từ góc nhìn khác, bất kỳ ý kiến ​​nào đều được chào đón.

Trả lời

6

Trong mỗi trường hợp, bạn re dequeueing hai ô cho mỗi hàng. Trong trường hợp 1 và 2, trước tiên bạn gọi phiên bản ("Cell", forIndexPath: indexPath).Trong trường hợp này, chế độ xem bảng kết thúc bằng hai ô cho mỗi hàng, một ô chồng chéo hoàn toàn và che khuất nhau. Bạn có thể thấy điều này trong thanh tra xem vì bạn có thể sửa đổi góc nhìn để xem đằng sau:

enter image description here

(tôi sửa đổi mã cellForRowAtIndexPath như thế này:

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 
} 

để đưa nhãn văn bản khác nhau cho mỗi ô.) Trong trường hợp 3 và 4, trước tiên bạn gọi phiên bản ("Cell"), chế độ xem bảng chỉ có một ô cho mỗi hàng.

Tại sao lại có hành vi khác? Nếu bạn tạo một lớp con tùy chỉnh của UITableViewCell và sử dụng phân lớp đó trong bảng phân cảnh của mình, bạn có thể ghi đè lên các phương thức khác nhau và thêm câu lệnh print() để xem điều gì đang xảy ra. Cụ thể, awakeFromNib, didMoveToSuperViewdeinit. Những gì transpires là trong trường hợp 1 và 2, ô đầu tiên được tạo ra (awakeFromNib) và ngay lập tức được thêm (didMoveToSuperView) vào một superview, có lẽ là xem bảng hoặc một trong các subviews của nó. Trong trường hợp 3 và 4, ô đầu tiên được tạo nhưng không được thêm vào superview. Thay vào đó một thời gian sau đó, tế bào được deallocated (deinit).

(Lưu ý rằng nếu ô thứ hai được dequeued sử dụng phiên bản ("Cell", forIndexPath: indexPath), nó quá được bổ sung ngay cho một SuperView. Tuy nhiên, nếu các tế bào thứ hai được dequeued sử dụng phiên bản ("Cell"), nó chỉ thêm vào một SuperView sau Phương pháp cellForRowAtIndexPath đã trả về.)

Vì vậy, sự khác biệt chính là kết quả phiên bản ("Cell", forIndexPath: indexPath) trong ô được thêm ngay vào chế độ xem bảng, trước khi cellForRowAtIndexPath hoàn tất. Điều này được gợi ý trong câu hỏi/câu trả lời mà bạn giới thiệu, vì nó chỉ ra rằng ô đã khử sẽ được định kích thước chính xác.

Sau khi được thêm vào phần giám sát, ô đầu tiên không thể được phân phối vì vẫn có một tham chiếu mạnh mẽ đến nó từ trình giám sát của nó. Nếu các tế bào được dequeued với phiên bản ("Cell"), chúng không được thêm vào superview, do đó không có tham chiếu mạnh mẽ cho họ khi biến số cell được gán lại và do đó chúng được giải phóng.

Hy vọng tất cả những điều đó có ý nghĩa.

+0

Câu trả lời của bạn là tuyệt vời, cảm ơn một bó! – fujianjin6471

1

dequeueReusableCellWithIdentifier: không đảm bảo cho bạn: ô có thể là nil, vì vậy bạn phải kiểm tra xem ô của bạn có đang là nil và xử lý đúng hay ứng dụng của bạn sẽ bị lỗi.

dequeueReusableCellWithIdentifier:forIndexPath:, mặt khác, không kiểm tra điều này cho bạn (nó luôn trả về một ô).

Đối với trường hợp cụ thể của bạn (Swift), điều này có nghĩa là bạn có thể tháo gỡ an toàn tế bào với dequeueReusableCellWithIdentifier:forIndexPath:, trong khi bạn sẽ phải sử dụng cú pháp if let với cú pháp thứ hai.

mã Ví dụ (trong Objective-C, tôi không sử dụng 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

Tại sao không có trường hợp 1, 2 hoạt động như mong đợi? – fujianjin6471

+0

Không chắc chắn (Tôi không thích Swift vì vậy tôi chưa đào sâu vào nó), nhưng có vẻ như thuộc tính 'textLabel' của bạn không phải là điều kiện nhưng thực tế là' UITextField', vì vậy hãy cố gắng mở nó ra ('?') sẽ thất bại, vì nó là một đối tượng thực tế và không phải là một trình bao bọc. Bạn có thể thấy rằng ô thực tế ** có **, bởi vì nếu không ứng dụng của bạn sẽ gặp sự cố (trả về 'nil' trong' tableView: cellForRowAtIndexPath: 'tăng một ngoại lệ và treo khi trả về' nil'). Hãy thử 'cell.textLabel.text' mà không có'? 'Hoặc một hỗn hợp của điều kiện và lực unwrapping ('? 'Và'! '). –

+0

Cảm ơn, nhưng tôi nghĩ điều gì có ý nghĩa không phải là ngôn ngữ chúng tôi sử dụng. @ pbasdf của câu trả lời khai sáng cho tôi, nó thực sự giúp – fujianjin6471

Các vấn đề liên quan