Detecting references for a Swift object allocated by a storyboard - ios

I am developing a game for iOS using Swift. The game is played in rounds, and when a round is over, it is no longer needed. So unlike conventional applications where a storyboard scene may stick around when someone navigates away from it, I want the scene for the round to be destroyed when the game navigates to the scene following the round.
I am pretty confident I have figured out a way to do this because I’m instantiating certain objects in the scene and I have verified that the deinit method for these objects gets call when the round ends and navigation to the next scene occurs. The problem is that for one of the objects, the deinit method is not being called, and I have not been able to figure out why. The storyboard scene should be the only thing referencing the object, and searching through to source code has not revealed any other usages. It seems like something beside the storyboard scene somehow has a reference to the object and keeps it alive when the scene goes away.
I have verified the object is being created by setting a breakpoint in its init method, and that breakpoint gets hit when starting the round.
I’ve tried using the Allocations instrument to track what might be referencing the object. However when I run the game and finish the round. I can find no evidence that the Allocation instrument ever saw the object at all. I suspect the instrument may not track objects that are created by a storyboard scene, especially since I can not find traces of the other storyboard objects that do get deleted with the storyboard.
What can be done to determine why this object seems to survive past the lifetime of the storyboard scene that should own it?

I think found out why the object was not being destroyed. In it's initializer it was applying a closure to something else. The closure contained a reference to a member of the object, and so I think the closure was capturing self strongly even though self was never explicitly needed for things to compile successfully. This might be a bug with the Swift compiler; it could have emitted a warning or error about this; it would have been easier the find and/or prevent the problem. Anyway I changed the closure so that it captures the object's member as unowned and now the object appears to get destroyed.

Related

Return the owner of a variable - Swift

So I have objects that in turn have sprites associated to them. a snippet of my Object class:
import SpriteKit
class Block {
var sprite : ColourSprite
}
So as you can see, it has a variable that is in fact a SKSprite (ColourSprite is my custom class that inherits from SKSpriteNode).
Now, at some points during the game, these sprites are deleted (i.e sprite.removeFromParent()), but the objects are obviously still somewhere.
I want to be able to send the objects to garbage collection once their sprites are gone. I am thinking that I can do something like sprite.getOwner() but I can't seem to find it. Is this possible?
The only other option I can think of is to manually check all objects and check each one's sprite but I feel that is long and wasteful.
You can check whether the Blocks are still in memory by using Xcode 8.3's new debug panel.
Just after you remove your sprites pause the program and go to that panel. See if there is any Block instances in the left panel. If there is, click on it to check what is retaining it.
If for example your GameScene is retaining the Block, you go to your GameScene and find the property. Then you can just set that to nil after you remove your sprite.

Swift - garbage collection - something gets left behind

I'm fairly new to iOS development, so I apologize in advance if my question sounds not worthy of being on stackoverfow.
I am building a simple game with a home ViewController and a gameplay ViewController as far as the structure is concerned.
I've added a very simple function to kill activities to make sure nothing gets left behind, which gets called right before it leaves the gameplay ViewController. the following is the code I'm using:
private func cleanThis(){
//removing objects from array
activeEnemies.removeAll()
//removing objects from array
activeTargets.removeAll()
//removing the rest
let subViews = self.view.subviews
for subview in subViews{
subview.removeFromSuperview()
}
}
I must be missing something here, and when I test it without moving on to the homepage, there's a constant memory activity of 38MB. And I haven't figured out yet as to how to monitor what is still left in the ViewController.
Any help is much appreciated.
p.s. when there's no objects in ViewController, the memory activity should be 0, is this correct?
iOS doesn't use a garbage collector. It uses a something called automatic reference counting. The main way you get into trouble is cyclical references (A has a strong reference to B which has a strong reference to A).
Xcode Instruments will show you all memory allocations and deallocations, and can show memory leaks as well. Here's a screenshot to demonstrate:

ARC not releasing live bytes

Questions about the general model of how I should be programming in iOS.
I started before ARC, doing manual memory management. I was originally taught to make every class variable a property and release in dealloc. This model works great, when I push and pop navigation controllers LIVE BYTES alloc and dealloc respectively.
When I switched to ARC however this is not the case. My live bytes never seem to go down, even when popping a navigation controller. I don't get it, am I not supposed to use properties? I generally use a strong property for anything except for an IBOutlet in which I case I'll use a weak property.
Is there something I'm missing? Do I need to do something in viewDidUnload or implement my own dealloc method???
If I use my app for long enough, I'll eventually receive a memory warning and crash. So I know something's not right.

Memory/resource management using MonoTouch and MonoTouch.Dialog

I have a MonoTouch app that has a UITabBarController, with each of the tabs being a UINavigationController. Some of these wrap a UIViewController which adds a UITableView and a UIToolbar, and others wrap a DialogViewController.
I've not paid much attention to memory / view management thus far (I've been mostly running in the simulator), but as I've started testing on a real device, I've noticed some failures due to low memory conditions (e.g. the app gets terminated, and I discover from my log that DidReceiveMemoryWarning got called prior to this). Other times I notice prolonged pauses in the app's responsiveness that I am assuming are due to a GC cycle.
Thus far I've been assuming that every DialogViewController that I push onto the nav stack will clean up its views and other things it's allocated when I pop it. But I am starting to realize that it's probably not that easy, and that I need to start calling Dispose() on things.
Are there best practices for how to deal with managing resources and memory with MonoTouch and MT.D? Specifically:
Is it required to call Dispose on a DialogViewController after it's popped? If so, where is it best to do this? (ViewDidUnload? DidReceiveMemoryWarning? destructor?)
Does the DVC automatically dispose objects like the RootElement that is passed to it or do I need to worry about this? How about UIImages that it loads as part of rendering a table cell (e.g. StyledStringElement)?
Are there places where I should call GC.Collect() to better space out collections so as to not take a bit hit in responsiveness when a GC does happen?
Does the generational garbage collector help with the interactivity problems and is it stable enough to use in a production app? (I believe it's still billed as "experimental" in MonoDevelop 3.0.2 / MT 4.3.3)
What do I need to do in DidReceiveMemoryWarning to reduce the likelihood that iOS will shoot my app? Since each non-visible view controller seems to get this call, I'm assuming that I should clean up that view controller's resources... should I do the same kinds of things I do in ViewDidUnload?
I don't seem to get my ViewDidUnload called (even after I get a DidReceiveMemoryWarning). In fact I don't recall ever seeing it in my log. If iOS always called my ViewDidUnload after DidReceiveMemoryWarning, I could just do all the cleanup in ViewDidUnload... What is the best way to split cleanup responsibility between ViewDidUnload and DidReceiveMemoryWarning?
I apologize for the general nature of this question - this seems like a good topic for a whitepaper, but I couldn't find any...
Update: to make the question more concrete: after using Instruments and the Xamarin Heapshot profiler, it's clear to me that I'm leaking UIViewControllers when the user pops the navigation stack. Rolf filed a bug for this and it has two dups, so this is a real issue for more than just me. Unfortunately I haven't found a good workaround for the leaked UIViewControllers - I have not found a good place to call Dispose() on them. The natural place to free resources allocated by ViewDidLoad is in the ViewDidUnload message, but it never gets called on the simulator so my memory footprint keeps growing. On the device, I do see DidReceiveMemoryWarning, but I am reluctant to use this as the place to free my viewcontroller and its resources since I am not guaranteed that iOS will actually unload my view, and therefore not guaranteed that my ViewDidLoad will get called again either (leading to a ViewDidAppear which would need to code defensively against situations where its underlying resources were disposed). I'd love to get some advice on how to get out of this mess...
I've spent a couple of days in the MT.D source code and in the profiler. While I am still looking for general guidance on what the best design pattern is for implementing DidReceiveMemoryWarning and ViewDidUnload, I do have some general observations to share that could be useful for someone:
MonoTouch.Dialog is very well behaved. It does not leak any resources under ordinary usage. It keeps a control tree under DVC.Root, and each Element's Dispose method correctly Disposes the underlying UIKit control. You don't even have to worry about disposing an old RootElement if you've replaced DVC.Root - the property setter automatically disposes it for you. Overall, MT.D doesn't appear to suffer from any significant memory issues. There is one exception - see below.
When creating your own custom Elements (e.g. MultilineEntryElement), make sure to override the Dispose(bool) method, disposing the underlying UIKit control (e.g. UITextView), and chain the base class Dispose() method. The source code in Miguel's MT.D github project provides plenty of good examples. All the Elements implement the standard Dispose pattern (although they omit a destructor/finalizer that calls Dispose(false)).
When implementing custom view controllers, it is generally not necessary to implement Dispose on UIViewController subclasses, nor on TableView DataSource or Delegate classes. When the view controller gets GC'ed, it will correctly call Dispose on its references. All the cells that you allocate in the DataSource will be properly disposed.
As an exception to (3) - I encountered a nasty issue when adding my own subview to a TableView's cell. This subview is a control I created called "UICheckbox" that ultimately inherits from UIImageView, which has two UIImages (on and off) and a public event called Clicked. I only experience an issue when an event handler which references members of the DataSource is hooked to this event (if the event handler doesn't reference the DataSource or controller itself, all is well). However, when the conditions above are met, and the controller is dismissed, there is apparently some cycle that the GC can't figure out, and every UICheckbox I put on the TableView is leaked (along with its images). The only way I found to work around this was to add code to ViewDidDisappear to dispose of the ViewController and clean up its state IFF it is no longer anywhere in the navigation stack. It's hacky but it works.
In general, I adhere to the following template for allocating objects in my view controllers:
allocate nothing in the constructor (use it only to pass state in)
create a control tree in ViewDidLoad (and dispose it in ViewDidUnload). think "InitializeComponent" in XAML (if that helps). If the UIViewController is going to push a DialogViewController onto the nav stack, the ViewDidLoad is a good place to create the DVC.
initialize values in the control tree in ViewDidAppear. E.g. you can add/delete/replace Elements, Sections, and even the Root of the DVC in this method. But don't create a new DVC.
There is a general issue with leaking ViewControllers when the user navigates up the nav stack (I reference the bugzilla link in the "Update" in the question). This also affects MT.D. There is a fairly straightforward workaround - add the following line of code in ViewDidAppear of the parent view controller:
// HACK: touch the ViewControllers array to refresh it (in case the user popped the nav stack)
// this is to work around a bug in monotouch (https://bugzilla.xamarin.com/show_bug.cgi?id=1889)
// where the UINavigationController leaks UIViewControllers when the user pops the nav stack
int count = this.NavigationController.ViewControllers.Length;
Rolf does a great job explaining why this bug happens and why the workaround works in the bugzilla link, so I won't repeat it.
I hope someone finds this useful. I also hope someone smarter than me has some guidance on how to handle DidReceiveMemoryWarning and how to split work up between that method and ViewDidUnload.
Update:
A couple more notes:
I now realize the protocol for DidReceiveMemoryWarning and ViewDidUnload: the former is always delivered to every view controller, while the latter is only sent for view controllers that aren't currently displaying, AND aren't deeper than the root of the navigation stack. In the end, I decided to ignore DidReceiveMemoryWarning because I don't really have images that I cache and can dump (as per the iOS guidance). In ViewDidUnload, I release all the resources I allocated in ViewDidLoad.
My app has a TabBar where each tab hosts a UINavigationController, most of which push a DialogViewController. One issue I was dealing with was leaking the DialogViewController after the ViewDidUnload let go of the reference to it. I tried Disposing the DVC in ViewDidUnload, but iOS kept on wanting to reinvoke it and I was getting an exception for invoking a selector on a GC'ed object. I discovered the reason - the navigation controller was holding onto the DVC in its ViewControllers array. The solution is to release the array by creating a zero-length array in its place - in ViewDidUnload:
this.ViewControllers = new UIViewController[0];
The old array will now be GC'ed, and so will the DVC because nothing is pointing to it anymore. And iOS won't ever reinvoke the object. Note - no need to call Dispose on the DVC.

App Crashes if AddAnnotations doesn't finish

I have an Application, which is a SplitViewController that has a master view on the left and the detail view on the right. One of the views (Branch Finder) is a Map view that loads a series of Annotations to the Map.
If I let the annotations load before switching to any other view (loading the annotations take takes all of 1 second) then everything is fine. However, if the user quickly switches off the Branch Finder view, whilst the annotations are being loaded, then the App will crash with the following notice:
[BranchFinder_iPad respondsToSelector:]: message sent to deallocated instance 0x807d230
Now, my thoughts are that the deallocated instance would refer to the Array (declared in the header of the view) that contains all the annotations being released and set to nil when the user leaves the BranchFinder_iPad view. This is the array that is being passed to the addAnnotations method.
[self.mapView addAnnotations:branchSites];
Has anyone else encountered an issue where leaving a view, mid-way in the add allocations and a crash occurs if the user moves to another view.
Just to clarify:
If I wait for the annotations to load, switching to any other view causes no problem.
I did have a custom annotation view, but I stripped that out of my code (to eliminate it from the mix). Doing this has not changed anything.
I have looked elsewhere for help on this issue, but a lot of the view tutorials regarding map views are single view only, so this issue hasn't arisen.
I have found a vaguely similar issue # the following: mapkit addAnnotations crashes
And finally, I have just made the jump to x-code 4. I think some of my problems are just because I'm relearning some of the things I should know.
Regards,
Nathan A
PS: I wanted to attach an image to this, but am having trouble. I don't have the reputation points to do it natively, and my workplace doesn't allow me access to any image hosting portals. I will endeavour to add an image later today.
Hey anyone who reads this.
I basically performed a rookie mistake here - for the MKMapView in my application, I had to set the delegate to nil as part of the deallocation routine within my view. THe apple documentation makes mention of this in the below document:
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapViewDelegate_Protocol/MKMapViewDelegate/MKMapViewDelegate.html
For the relevant section:
Before releasing an MKMapView object for which you have set a delegate, remember to set that object’s delegate property to nil. One place you can do this is in the dealloc method where you dispose of the map view.
Not having this was only causing an issue if I switched to another view AND if the MKMapView was still being referenced in executing code, such as the addAnnotations routine.

Resources