Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

tableView.dequeueReusableCellWithIdentifier() causes app to hang

ORIGINAL POST

We have recently converted our app to Swift 2.0, and iOS9. One strange issue I am seeing is that calls to tableView.dequeueReusableCellWithIdentifier() cause the app to hang in the simulator.


The code
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    //hangs on the line below
    let headersection: HeaderSectionCell = tableView.dequeueReusableCellWithIdentifier("SectionHeader") as! HeaderSectionCell 

    ...

    return headersection
}

The header cell

class HeaderSectionCell: UITableViewCell {

    @IBOutlet var labelOne: UITextView!
    @IBOutlet var labelTwo: UITextView!
    @IBOutlet var textView: UITextView!

}

The simulator CPU usage pegs at 100%

CPU Pegging


After hitting pause in Xcode, it shows me its hanging on this Swift function.

Pausing while hanging shows us here


Here are some of the routines where iOS is looping under the covers.

enter image description here

enter image description here

Finally, our Swift call to dequeueReusableCellWithIdentifier() enter image description here

This particular hanging instance is from the function tableView(tableView: UITableView, viewForHeaderInSection section: Int), but we're also hanging inside of a call to tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath), with the same issue.

I've tried playing around with the cell properties in storyboard editor, but nothing sticks out as anything different from other views that are working fine.

EDIT

It appears that there is an infinite loop between Foundation and libobjc.A.dylib, underneath that call to dequeReusableCellWithIdentifier(). I ended up making sure Foundation was imported before any other framework, and abstracted the offending UITableViewCell into its own class (was being reused). The original call in question is now working, but there is another one that is still looping under the Swift covers that I am working to figure out.

Hitting pause in the infinite loop puts me in the same assembly stack locations:

Top of stack trace after pause:

libobjc.A.dylib`objc_msgSend:
    0x107f6a800 <+0>:   testq  %rdi, %rdi
    0x107f6a803 <+3>:   jle    0x107f6a850               ; <+80>
    0x107f6a805 <+5>:   movq   (%rdi), %r11
    0x107f6a808 <+8>:   movq   %rsi, %r10
    0x107f6a80b <+11>:  andl   0x18(%r11), %r10d
    0x107f6a80f <+15>:  shlq   $0x4, %r10
    0x107f6a813 <+19>:  addq   0x10(%r11), %r10
    0x107f6a817 <+23>:  cmpq   (%r10), %rsi
    0x107f6a81a <+26>:  jne    0x107f6a820               ; <+32>
->  0x107f6a81c <+28>:  jmpq   *0x8(%r10)

Top of stack trace after another pause:

Foundation`-[NSLocalizableString length]:
    0x1071c5cbc <+0>:  pushq  %rbp
    0x1071c5cbd <+1>:  movq   %rsp, %rbp
->  0x1071c5cc0 <+4>:  movq   0x80461(%rip), %rax       ; NSLocalizableString._developmentLanguageString
    0x1071c5cc7 <+11>: movq   (%rdi,%rax), %rdi
    0x1071c5ccb <+15>: movq   0x7436e(%rip), %rsi       ; "length"
    0x1071c5cd2 <+22>: popq   %rbp
    0x1071c5cd3 <+23>: jmpq   *0x8ea77(%rip)            ; (void *)0x0000000107f6a800: objc_msgSend

It's just looping back and forth between these two lower level routines, consuming 100% of the simulator CPU.

like image 410
tyler Avatar asked Sep 15 '15 19:09

tyler


People also ask

What is dequeuereusablecellwithidentifier and how does it work?

The solution to the issue above is what we’ve been using all along! dequeueReusableCellWithIdentifier. Instead of creating every single cell and then selectively displaying them, we only create a handful of cells, enough to fill the screen and a little more. As we scroll, we reuse the cells offscreen, leading to a much more memory efficient task.

How do I dequeue a cell in a table view?

A table view maintains a queue or list of UITableViewCell objects that the data source has marked for reuse. Call this method from your data source object when asked to provide a new cell for the table view. This method dequeues an existing cell if one is available or creates a new one using the class or nib file you previously registered.

When should a uitableviewcell object be reused?

For performance reasons, a table view’s data source should generally reuse UITableViewCell objects when it assigns cells to rows in its tableView:cellForRowAtIndexPath: method. A table view maintains a queue or list of UITableViewCell objects that the data source has marked for reuse.

What is the purpose of the uitableviewcell function?

This function is called before cell reuse, letting you cancel current requests and perform a ‘reset’. Below I show you what a custom table view cell might look like. Add the code below to the UITableViewCell and you should be good to go.


3 Answers

It was a String localization issue. The UITableViewCell contained UITextField with a non-empty value for the Text property, that didn't have English checked in the Localization properties of the UITextView.

Checking English fixed the issue.

What a pain! Go figure, why not infinitely loop instead of throw an intelligible error?

like image 55
tyler Avatar answered Oct 13 '22 23:10

tyler


My solution was similar to styler1972's.

In my case, I was upgrading a combination Swift / Objective-C project from Swift 1.2 to Swift 2.0. The app ran fine on iOS 9 prior to the upgrade to Swift 2.0. After the upgrade to Swift 2.0 the app would enter an infinite loop when segueing from a Table View Cell Accessory Push to a View Controller. Running the app in the simulator, then pausing it when caught in the infinite loop left me at [NSLocalizableString length] (which led me to this post).

My fix was to do the following.

  • Remove the Main.storyboard file from the en.lproj directory.
  • Remove the en.lproj directory, including the InfoPlist.strings file.
  • Place Main.storyboard in the root project directory.
  • Update the project to reflect the above changes.
  • In the project settings under Info / Localization, remove all settings.
  • Clean the build directory (for good measure).
  • Clean the build (for good measure).
  • Build and run.

Infinite loop no longer occurs.

like image 42
Justin Domnitz Avatar answered Oct 13 '22 22:10

Justin Domnitz


Make sure to backup your project files to restore the localization files later on.

  • In Project Settings, Localizations, remove all localizations except the project development language(which was English for me).
  • Clean and run. At this point you should not see the error anymore.
  • Add the localizations you removed above and restore the localizations from your backup.
like image 43
Elsint Avatar answered Oct 14 '22 00:10

Elsint