3

I already know the solution for my problem, but I just really don't understand what's going on here. I have a UITableViewController that takes the cell in didSelectRow and uses it for something in an other function. I pass the cell as AnyObject?. Now when I drill down into a detail VC and then back up again, repeat those steps 2 more times, my app crashes.

First I thought it's a problem somewhere else in my app, but then I created a sample project with nothing but those few lines and managed to reproduce the bug. I also already filed a radar, but I don't want to die dump (as you never hear back form those radar guys) ;) Can anybody explain to me what's going on here!

final class MasterViewController: UITableViewController {

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let cell = tableView.cellForRow(at: indexPath)
        doSomethingWith(sender: cell)
    }

}

extension MasterViewController {
    func doSomethingWith(sender: AnyObject?) {
        // I don't even use that sender!!!
        // If the function would read    doSomethingWith(sender: Any?)     everything would be ok!
    }
}

If you want you could download the whole sample at: http://www.georgbachmann.com/demo/CrashTest.zip

I'd really like to know what the swift compiler is doing there!

::EDIT::

I just noted that the crash also depends on doSomethingWith: being in an extension!

Georg
  • 3,664
  • 3
  • 34
  • 75
  • Why not pass your datasource and `indexPath` instead of the cell? – kye Sep 21 '16 at 15:13
  • That's not the question ;) In the real app of course I use the object behind a datasource using the IndexPath, but I want to display a popover and therefor need the cell itself. But again, that wasn't the question. The question is what's going on with swift. Why is it causing that crash? Why does AnyObject vs Any make such a difference even if the object isn't even used?!? – Georg Sep 21 '16 at 15:21
  • I also know what fixes the crash... I just want to understand it... – Georg Sep 21 '16 at 15:22
  • I apologize, guess i misunderstood the question – kye Sep 21 '16 at 15:23
  • What error do you get when it crashes? – Mike Taverne Sep 21 '16 at 15:24
  • EXC_BAD_ACCESS I also just noted that when cleaning up my sample I did move the `doSomethingWith` function into the main class itself. It also works fine that way... When you move it out into an extension, then the problem occurs. I updated the code above and the sample code – Georg Sep 21 '16 at 15:40
  • Why pass the cell if you are not using it? Just pass nil – ohr Sep 21 '16 at 15:44
  • This looks like a bug in Swift to me. – Craig Grummitt Sep 21 '16 at 15:55
  • @ohr this is just a sample to demonstrate a bug... i want to know WHY the crash occurs... – Georg Sep 21 '16 at 16:21

1 Answers1

0

Basically, you don't "own" the reusable cells. The UITableView owns them. Since you're passing the same cell three times to the function with strong reference (you wrote, you know the solution, so I'm omitting it), you increase the ref count of the cell (in this case to be +3, but it doesn't matter). At some point the UITableView decides to release its cells, but your UIViewController has a strong reference to them. At this point the app crashes. No bug here - you should never pass objects that you don't own.

Max Pevsner
  • 4,098
  • 2
  • 18
  • 32
  • If you have a look at the code above, the cell that is passed into the function is not even used. Still it crashes!. I don't keep a reference what so ever. So there are no retain-counts that could be the problem. There is no reason for the TableView to release my cell. And even if it would do so, it should then create a new one when it's used again. But not just crash ;) It has something todo with it being passed as AnyObject vs. Any.. – Georg Sep 21 '16 at 15:34
  • @Georg I'm not going to argue with you. I gave you the correct answer. In your project you print "Why oh why" when the cell is being deallocated. You can read about the memory management in iOS here: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html. – Max Pevsner Sep 21 '16 at 17:45
  • Sure, but just passing an object into a function does not make it be owned. It's not kept inside that function (it's not even used). So there is no ref-count that is increased on that cell. And if my VC would have a strong reference (which it doesn't) then the cell would also not be released. I guess the whole bug here is a problem caused by the swift compiler and I would like to know what it is. It's no problem if my function lives inside the main class, but having it inside an extension and using AnyObject instead of Any causes the problem. – Georg Sep 21 '16 at 17:56