2

One of my UIViewController has several child view controllers. They are built inside the interface builder, by dragging an NSObject to the "Objects" section at left, then put in my own SpecialViewController as "Custom Class". With this setup, upon viewDidLoad I'll have my views and the controller ready. The workflow is suggested by the following screenshot:

Drag "Object" to create instance

And in my implementation, I have:

@interface ParentController : UIViewController
{
     SpecialViewController *svc;
}
@property (nonatomic, retain) IBOutlet SpecialViewController *svc;

As I understand that during didReceiveMemoryWarning I should release my own resources. Then set IBOutlets to nil during viewDidUnload.

I get crashes when simulating low memory in the simulator, with debugger pausing at didReceiveMemoryWarning of SpecialViewController (whose body is just [super didReceiveMemoryWarning];), with error EXC_BAD_ACCESS (code=1, address=0xe0000008). At this time, the parent controller isn't visible, so it can be safely released.

Parent controller also contains only [super didReceiveMemoryWarning]; in didReceiveMemoryWarning. I've tried niling IBOutlets in both classes. It didn't help.

Any idea why this happened?

I'm targeting iOS 4 & 5 with ARC. SpecialViewController is a subclass of UITableViewController.

Through tracing, I found that ParentController didReceiveMemoryWarning is called before SpecialViewController.

He Shiming
  • 5,710
  • 5
  • 38
  • 68
  • 1
    Release object and set object to nil are not equal. If u catch memory warning, u must to release **unused** resources. Try to `[svc release]` first. – Feo May 03 '12 at 10:11
  • 1
    @Feo, Hmm... I'm using ARC. I did release unused resources. Those are the ones I'm managing in my own classes, correct? – He Shiming May 03 '12 at 10:18
  • Are you doing `self.scv=nil`? – Rui Peres May 03 '12 at 10:22
  • @JackyBoy, yes I'm positive I did so. – He Shiming May 03 '12 at 10:23
  • you should release and assign the outlets to nil in both of `dealloc` and `viewDidUnload` .. are you doing that ? – Malek_Jundi May 03 '12 at 10:44
  • @Malek_Jundi, I believe Cocoa is doing releases in dealloc and viewDidUnload considering I'm using ARC. The problem here is with ``didReceiveMemoryWarning``. It's not dealloced or unloaded yet. – He Shiming May 03 '12 at 10:47
  • sorry I didn't see that you are using ARC ,, I thinks the retain key in ARC is similar to strong key .. and since svc is not a top level object it should hold the weak property . did you try that ? – Malek_Jundi May 03 '12 at 11:03
  • try to replace `retain` attribute to `strong` – Feo May 03 '12 at 11:04
  • @Malek_Jundi, I didn't understand. How is it weak? It's managed by Cocoa through the .xib file. I didn't alloc ``svc`` myself. – He Shiming May 03 '12 at 11:07
  • change the `@property (nonatomic, retain) IBOutlet SpecialViewController *svc;` To `@property (nonatomic, weak) IBOutlet SpecialViewController *svc;` – Malek_Jundi May 03 '12 at 11:09
  • @Feo, replacing it to ``strong`` didn't appear to have any effect. It's the same crash. I should point out that I'm able to reproduce this crash every time, in the exact same place. – He Shiming May 03 '12 at 11:09
  • @Malek_Jundi , this produces ``Existing ivar 'svc' for __weak property 'svc' must be __weak`` compile error. I'm positive that it's not a weak pointer problem. This is an ``IBOutlet`` instance. It's supposed to be retained. – He Shiming May 03 '12 at 11:15
  • you need to put __weak before the instance variable also (the one you are define in the interface) , and no its supposed to be retained .. you always need to retain just the top level objects (the one that don't have parent view). – Malek_Jundi May 03 '12 at 11:24
  • check This [link](http://stackoverflow.com/questions/7678469/should-iboutlets-be-strong-or-weak-under-arc) – Malek_Jundi May 03 '12 at 11:34
  • @Malek_Jundi, sorry I forgot to mention that I'm targeting iOS 4 and 5. That post only addresses iOS 5. I'm not aware of that this requires iOS 5 to be done? – He Shiming May 03 '12 at 11:41
  • ok in this case you implementation is right , now I just want you to try to assign the value instead of retain it , and check if your problem will be solved . – Malek_Jundi May 03 '12 at 11:59
  • @Malek_Jundi, ``assign`` is the equivalent of ``unsafe_retained``. Now this crashes my app, as I mentioned before, I'm not creating the instances of ``svc`` myself. – He Shiming May 03 '12 at 12:38
  • Is SpecialViewController a subclass of UIViewController? – lnafziger May 04 '12 at 00:15
  • @lnafziger, it's actually a subclass of UITableViewController. I'll edit the question to reflect this. – He Shiming May 04 '12 at 01:09
  • @HeShiming i am facing with same problem but i am using different view controller. can you check my question http://stackoverflow.com/questions/16806441/get-didreceivememorywarning-and-crash-application. Thanx in advance – Kalpesh May 29 '13 at 06:58

2 Answers2

2

It seems like you have a view controller with in a view controller here. Is there any particular reason that you have chosen to create the class like this? In my experience each UIViewController should be a separate subclass. Based on the fact that your error arises in didReceiveMemoryWarning, I believe that the issue is elsewhere. Can you share your initialization code for this View Controller?

If you are attempting something like UIViewController Containment, you should probably check out the WWDC topic that covers this process.

Sam
  • 2,579
  • 17
  • 27
  • I'm not aware that this is a weird approach. The ``initWithNibName`` method isn't doing anything besides calling super. I recall seeing this method somewhere but can't really get the link now. It's designed this way because the UI has 3 distinct table views though they aren't displayed at the same time. Bad design? – He Shiming May 07 '12 at 14:50
  • Yes, very likely a bad design. How about 3 UITableViews contained within 1 UIViewController. Programmatically toggle the `hidden` property on 2 out of the 3 UITableViews to show the correct one. – Sam May 07 '12 at 14:57
  • you mean I should do it without initiating 3 ``UITableViewControllers``? Just manage them separately via my own delegates? Ah, I just got it. I shouldn't be using extra controllers. I just need the delegates. Thank you. I'll revise my code, and hopefully accept your answer shortly. – He Shiming May 07 '12 at 14:59
  • Perfect! I would even use a UIViewController instead of a UITableViewController. Not sure if a UITableViewController supports multiple UITableViews. Let me know if I can provide any more help. – Sam May 07 '12 at 15:07
2

Update iOS > 6.0:

Views are no longer purged under low-memory conditions and so this method is never called [1].


View controller's didReceiveMemoryWarning default implementation will release their view; your responsibility is only to release any other views created in viewDidLoad or loadView, as well as any strong references to IBOutlet subviews.

You have a strong reference to another view controller, which will not be recreated when the view becomes visible again. You should not use the didReceiveMemoryWarning to release your the sec instance variable; instead, rely on the SpecialViewController's didReceiveMemoryWarning implementation to take care of releasing it's view.

In practice, I use didReceiveMemoryWarning to release my main view (self.view) and use viewDidUnload to release anything created in viewDidLoad. I find the balance of the names of the methods and their uses intuitive. If you create something in viewDidLoad, release it in viewDidUnload.

Tieme
  • 62,602
  • 20
  • 102
  • 156
Ash Furrow
  • 12,391
  • 3
  • 57
  • 92
  • Actually, it crashes the same way if I don't nil ``IBOutlets``. I think the niling process is there only for protection of my own code. It's theoretically not necessary, correct? But my point is that the controllers are embedded in the .xib. I'm not creating them in ``viewDidLoad``. – He Shiming May 07 '12 at 14:52
  • They're nil'd out so that you release as much memory as possible (setting to nil releases any previous instance). Since you're targeting iOS 4, you don't have zeroing weak references, so it's likely that a variable is being released without setting its reference to nil. Are you sure that you're not releasing `svc` in the view controller? And are you using `NSNotificationCenter` anywhere? I've seen that cause problems as well in iOS 4. – Ash Furrow May 07 '12 at 15:05
  • Under ARC plus the object is created in .xib, I don't think I can actually release it. I can't call release. But like I said, it's the same whether it's nil'ed or not. Doing nothing in ``didReceiveMemoryWarning`` crashes. It didn't get to ``viewDidUnload`` yet. – He Shiming May 07 '12 at 15:19
  • Setting a variable to `nil` under ARC still releases the object. Remember that you're not "doing nothing" in `didReceiveMemoryWarning`, you're calling super, which (unlike some other default implementations of `UIViewController` methods) does something: it releases your view. – Ash Furrow May 07 '12 at 15:24
  • But... it's a ``retained`` property. I understand the compiler will release it automatically. It's just not the case. I didn't nil it. But if ``[super didReceiveMemoryWarning]`` choose to release it, I can't prevent it can I? – He Shiming May 07 '12 at 15:26
  • You can, actually, by not calling `[super didReceiveMemoryWarning]`. You can include an empty implementation that does nothing explicitly. The property will be released whenever a new value is set, including nil. – Ash Furrow May 07 '12 at 15:47
  • Do you have any other reference saying I should not be calling ``[super didReceiveMemoryWarning]`` in this case? – He Shiming May 07 '12 at 15:55
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/10979/discussion-between-ash-furrow-and-he-shiming) – Ash Furrow May 07 '12 at 15:57