0

I'm slowly working on my first simple timer app, which is starting to slowly come to life. At the moment I've done no saving of data for when my app enters the background, for the moment whilst writing my app it is always staying loaded during my testing, even if its in the background for a small time. All this is being done on the simulator.

My problem is that I have a Table View Controller with an NSInteger index property

   @property NSInteger index;

that I use to manage the slow iteration step by step over an NSArray over time, by calling a doWork method.

Initial setup of my TVC and this property is performed as the view is prepared for segue from another view. More work is done on this property and other properties whenever my app receives local notifications or upon observing UIApplicationWillEnterForegroundNotifications.

All accessing of this property I've added debug logging around and I'm just confused with what I'm seeing

  • PrepareForSegue
    • @ 19:38:29:058 - calls a method doWork which increments my property from -1 to 0, and sets a local notification. Logs show self=0x10bb661f0
  • I press the home button to put my app in the background for ~10 secs
  • The local notification fires and I tap the banner to bring my app back to the focus
  • My TVC's awakeFromNib function sets up a notification observer as follows

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification 
                                                      object:nil
                                                       queue:mainQueue
                                                  usingBlock:^(NSNotification *note) {
                                                   [self doWork];
                                            }];
    
  • This notification fires twice! (for some reason?) each time causing my doWork method to be called

    • @ 19:38:45:832 - doWork immediately shows my property is now back to -1 not the 0 it was set to before being backgrounded, it is incremented again to 0. Also it shows self as different at self=0x10974bbd0, a value it remains at now whilst the app is foregrounded.
    • @ 19:38:45:836 - doWork is called again from this 2nd notification call, my property is correctly now still 0 from the last call, it is incremented again to 1.
  • after that my app delegate also gets it's didReceiveLocalNotification method called, which will also end up calling the same doWork method via another block in a Notification Center observer setup the same as the one detailed above.
    • @ 19:38:45:857 - doWork is called and again the property has gone from the 1 that was set, back to 0... and is again incremented back to 1.

I just can't understand what's going on. Most of the properties in my TVC are still presumably fine as the rest of my logic showing the TVC contents continues to work fine. Why is my NSInteger getting mangled so?

I thought perhaps there could be some issues with threading and the local notification handling occurring perhaps simultaneously, I hoped that adding the mainQueue would help with this, I'd previously got this set to nil. Sadly it didn't make any difference.

I wonder why self changes after being backgrounded for only a short amount of time. Naively I presumed that as everything seemed to be working on coming back to the foreground (the view still displayed, with all its data seemingly intact aside from this NSInteger property) that the self ptr and object would be the same. It's not too much to imagine that perhaps its been relocated by the OS somehow, but still this shouldn't have caused the property to change.

I'm using cocoa lumberjack for logging, and I've turned off ASYNC logging as follows

    #define LOG_ASYNC_ENABLED NO 

Which should at least mean that log calls block till they've logged. I could appreciate that if i did have some threading issues perhaps the logging order would be slightly in doubt. But for the initial corruption of the property described above after the app enters the background there is ~10 seconds from when I write 0 to the property, and later read -1 back out of it. This is clearly not a threading timing issue at that point.

Why am I notified about entering the foreground twice? If my properties were left where I set them it wouldn't matter much, but still strikes me as a little odd.

Is there a problem with my using self within this block, I've seen that sometimes you may want to use a weak self pointers for these blocks, but I've seen examples where self is used directly as well.

Any help on understanding and hopefully fixing this would be really appreciated. I'm a little stuck and can't see what I've done wrong!

Cheers

jimbobuk
  • 1,211
  • 12
  • 25
  • How many times is awakeFromNib getting called during all of this? – Gruntcakes Apr 10 '14 at 21:41
  • It certainly sounds as if you have two different objects involved in this, each getting their own notification. – Phillip Mills Apr 10 '14 at 21:45
  • Thanks you guys. You're both right. For some reason I totally forgot that awakeFromNib was called each time the view newly appeared. I'd not added any removing of observers into my view controller yet as eventually I will want to be able to move between a few new views whilst my code is active and those notifications are firing and working on my model. I realise now that I should just refactor and move the doWork method into my model, which can then be passed around any views that want to continue working with that aspect of model. – jimbobuk Apr 11 '14 at 09:29
  • So because I had left the notifications dangling, my old TVCs were hanging around and each one was showing their own alertbox upon notifications... going in and out of this TVC would keep adding a new extra alert as the dangling TVCs piled up. I've added in removeObserver calls within ViewWillDisappear and that worked. I've also changed my blocks for the notification to use weakself pointers instead of self directly. Thanks for your help guys. Feel free to answer this properly and i'll thank you for it. Otherwise I'll add my own stub answer just so its clearer to anyone finding this post. – jimbobuk Apr 11 '14 at 09:32

1 Answers1

0

So the answer to my problem as figured out by Woofbeans and Phillip Mills is that my awakeFromNib was being called each time i entered this TVC, and also I was failing to removeObserver on these notifications causing the stale undisplayed TVCs to stay around indefinitely. I'd not realised this key part of my repro of the problem.

Go into the TVC, come out, go back in and then you would have a duplicate of alerts in the app being caused by the fact that the original TVC was still around, being held onto by the strong reference to self within my own blocks.

I'm going to refactor this doWork functionality into my model so that it can persist and be handled better, independently of whatever view happens to be being displayed.

I also changed my block code to use a weakself pointer within the block, to hopefully stop that block from causing any object to persist purely because of the block being left behind.

Cheers everyone!

jimbobuk
  • 1,211
  • 12
  • 25