4

In my iphone project I always insert UITableView into the view controller as IBOutlet, most times it works well, but random crash will occur when do animation invoked by popToRootViewControllerAnimated. Track it by zombie, find the crash dues to UITableView instance has been deallocated, but there are still system events sent to it, so crash.

I always resolve such issue by either of the following methods in view controller's dealloc method.

tableView.dataSource = nil;   (work for most cases)
or
[tableView removeFromSuperview]; (work for some special cases)

Although the crash can be fixed by the above change, but I am still confusing.

  1. Is it apple's defect that we need to set its dataSource to nil explicitly to avoid crash? Or maybe our own app code has problem?
  2. Anyone who has also experienced such crash, do you know what's the root cause?

Any idea or discussion will be appreciated, thanks in advance.

enter code here
Exception Type: EXC_BAD_ACCESS (SIGSEGV) 
Exception Codes: KERN_INVALID_ADDRESS at 0x626f6d37 
Crashed Thread: 0 

Thread 0 name: Dispatch queue: com.apple.main-thread 
Thread 0 Crashed: 
0 libobjc.A.dylib 0x33fe0c98 objc_msgSend + 16 
1 UIKit 0x364538f6 -[UITableView(UITableViewInternal) _spacingForExtraSeparators] + 58 
2 UIKit 0x3645337a -[UITableView(_UITableViewPrivate) _adjustExtraSeparators] + 158 
3 UIKit 0x36453218 -[UITableView layoutSubviews] + 40 
4 UIKit 0x363ff5f4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 20 
5 CoreFoundation 0x30e13efc -[NSObject(NSObject) performSelector:withObject:] + 16 
6 QuartzCore 0x33d8dbae -[CALayer layoutSublayers] + 114 
7 QuartzCore 0x33d8d966 CALayerLayoutIfNeeded + 178 
8 QuartzCore 0x33d931be CA::Context::commit_transaction(CA::Transaction*) + 206 
9 QuartzCore 0x33d92fd0 CA::Transaction::commit() + 184 
10 QuartzCore 0x33d8c04e CA::Transaction::observer_callback(__CFRunLoopObserver*,     unsigned long, void*) + 50 
11 CoreFoundation 0x30e7da2e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 10 
12 CoreFoundation 0x30e7f45e __CFRunLoopDoObservers + 406 
13 CoreFoundation 0x30e80754 __CFRunLoopRun + 848 
14 CoreFoundation 0x30e10ebc CFRunLoopRunSpecific + 224 
15 CoreFoundation 0x30e10dc4 CFRunLoopRunInMode + 52 
16 GraphicsServices 0x34efe418 GSEventRunModal + 108 
17 GraphicsServices 0x34efe4c4 GSEventRun + 56 
18 UIKit 0x36428d62 -[UIApplication _run] + 398 
19 UIKit 0x36426800 UIApplicationMain + 664 
20 ScoutFree 0x00099558 0x1000 + 623960 
21 ScoutFree 0x00003618 0x1000 + 9752
jianhua
  • 1,011
  • 1
  • 12
  • 28
  • Yes, if `dataSource` or `delegate` is deallocated pointers to it should be set to `nil` before that. Removing from `superView` seems strange solution - maybe you're overlooking a discarded `delegate`. It's usually a good idea to create a special class that serves as `tableView` `datasource`/`delegate`. – Rok Jarc Apr 01 '12 at 11:09
  • Thanks guy. Do you mean it is necessary to set its delegate or dataSource to nil to avoid such crash? Why apple doesn't do such nil action when the table view dealloc. – jianhua Apr 03 '12 at 04:02
  • XCode doesn't know that the object being dealocated is also an active delegate/dataSource of some table, that's why it doesn't nil-out this references. Delegate/dataSource reference is _written_ in tableView. – Rok Jarc Apr 03 '12 at 07:35
  • So it may be the case view controller is deallocated, but the embedded UITableView is still active, referenced by other object, or auto-release, will be released in other life circle. – jianhua Apr 04 '12 at 09:29
  • Hmm, if `UITableView` is a subview of `viewController.view` this shouldn't happen. What i mean is: if `viewController` is _unloaded_ only it's `view` is usually deallocated. I haven't experienced a crash like you discribe but i always set `delegate` and `dataSource` to nil before dealocating them. But looking to your crash report: do you have any observers registered and deallocated before unregistering them? – Rok Jarc Apr 04 '12 at 09:53
  • Thanks for your kindly reply. The crash has higher reproducible rate if more animations undergoing at the same time. All external observers have been removed, I don't know how it is going inside the UITableView. – jianhua Apr 09 '12 at 01:16

2 Answers2

2

You forgot to set tableView.delegate to nil, so you can still get crashes, especially when animation is going(as it asks now dead controller for new rows). It's not Apple's defect, it's programmer responsibility to clear out those references. So set dataSource and delegate properties of tableView to nil, then release tableview(by setting corresponding property to nil or releasing iVar like this [_iVar release]; iVar = nil;)

Timur Kuchkarov
  • 1,155
  • 7
  • 21
1

First of all, the ONLY things you should be calling in dealloc are release on your ivars, unregistering for NSNotificationCenter notifications (if registered in init), or setting delegates of UIWebViews and UIScrollView to nil (as suggested by Apple's documentation). If your UIViewController is the delegate/data source of your tableview, there is no need to set those to nil in dealloc (or anywhere else), as when the view controller is destroyed, your guaranteed it won't send any rogue messages, and you're equally guaranteed that the delegate/data source of the table view won't get destroyed before the table view does.

It's highly unlikely that the defect is Apples. What OS are you targeting? If you're using ARC, you really should have few occasions when you need to be mucking around in dealloc. If you symbolicate your crash log, you will get the line numbers and classes from your app that is causing the crash. Symbolicating in Xcode 4 is really simple, you can find info on that here: Symbolicating iPhone App Crash Reports

What do you mean you always insert the tableview as an IBOutlet? If it's an IBOutlet, that implies that you have a table view in a nib file, in which case the table view gets created for you when the nib is unloaded. If you are trying to remove the table view and re-add it to the view for the purpose of updating its information, this is not the correct approach: simply calling reloadData will do this for you, and go through all of the delegate methods again. Are the delegate and data source an object OTHER than the view controller controlling your table?

Community
  • 1
  • 1
jmstone617
  • 5,707
  • 2
  • 24
  • 26
  • Thanks you guys's reply. 1. I agree with you that we should only manipulate vars on our own, not the system ones. 2. I don't use ARC. 3. I use UITableView as IBOutlet, loaded from XIB, view controller is its delegate. 4. Use tableView.dataSource = nil; to solve such random crash, I don't think I have found the root cause, just learn from crash call stack, set it to nil then it won't response to external event, so not crash. – jianhua Apr 06 '12 at 02:38
  • @jmstone You MUST set delegates/datasources to nil on dealloc when not using ARC, and I recommend to set them to nil on system classes even under ARC (as some of them are declared as unsafe_unretained instead of weak) – Timur Kuchkarov Aug 19 '13 at 05:43
  • There are certain cases, as I mentioned in my answer, where you should be setting your delegates to nil in dealloc, specifically in cases where you get a delegate callback from an async task (like webviews). You can be as risk-averse as you want and go crazy on setting delegates/datasources to nil. I've never seen a crash from a UITableView sending a delegate message to a deallocated UIViewController, so to me it's a waste of cycles. – jmstone617 Aug 19 '13 at 14:52