How to release everything thats retained by a viewController? - memory-management

I have 5 viewControllers which are reloaded each time I tap onto them. I want to reduce the memory allocations so in viewDidDisappear i am making all the IBOutlets nil , but still I want to reduce more memory,which is all that is retained by the controller. Will I have to do it individually or is there any method which will do the magic for me?

Setting IBOutlets to nil in viewDidDisappear releases nothing as these will all still be retained by the view.
You could release your other objects though, and then recreate in viewDidAppear but it's hard to understand why you would want to, just do it when asked in the didReceiveMemoryWarning method.
Declare your IBOutlets as weak and let the system decide when to release the view (and the outlets along with it) when it needs to.
Release any other objects you want to drop in low memory conditions inside the didReceiveMemoryWarning method. Once again the system decides when this is needed.

Related

Deleting CAMetalLayer for NSView

I have a custom NSView class which is layer backed. I make a CAMetalLayer for this NSView which is created in makeBackingLayer.
In makeBackingLayer I create a layer CAMetalLayer *backingLayer = [CAMetalLayer layer]; and set properties as required by me.
My question is, do I need to dealloc this layer explicitly while destructing NSView?
I do not create this layer in an overridden function, is it my responsibility to delete this or the NSView will take care of it?
I do not see any documentation around this. Moreover, all the samples that I see do not mention about deleting layers anywhere.
Thanks
You likely do not need to manually free your layer. Assuming your program has ARC (Automatic Reference Counting) enabled, your NSView should automatically free whichever CALayer is set to its .layer property upon its destruction.
If you're not sure if ARC is enabled, you can go to Build Settings in your Xcode project and search for Automatic Reference Counting. It's been on by default for new Xcode projects for several years now.
Note: Your NSView will only be able to free your layer if it is the only object holding a reference to it. If other objects in your program are holding references to your CAMetalLayer, your layer would not be freed until they remove their references.

IBOutlets in Swift are nil

(Developing on OSX 10.9, Xcode 6.1b)
If I declare IBOutlets in my AppController, everything is fine. I instantiate an object in InterfaceBuilder, drag it across to form an outlet, and by the time I have reached applicationDidFinishLaunching, my IBOutlets are populated and everything is great.
If I go a step further in abstraction and instantiate a custom controller object in InterfaceBuilder (a subclass of NSObject), and declare one of my objects as an IBOutlet in that class, they're nil, each and every one.
I can set the connection just fine, IB seems convinced it exists, the 'referenced outlets' list is correct, but it doesn't take, and I haven't been able to find anything in the documentation beyond
It is implicitly unwrapped because after your class is initialized from a storyboard or xib file, you can assume that the outlet has been connected.
Well, I was assuming that.
All of my code is boilerplate Xcode offerings:
#IBOutlet weak var sceneController: NSArrayController!
and I've checked and double-checked and triple-checked the connections. I've looked at dozens of iOS tutorials (although I cannot find the equivalent for OSX) all of which seems to be variations on the theme of 'yes, you can totally declare an outlet in a file other than the AppController, just make sure that every involved instance exists'.
(At the time of writing, the 'mac' documentation uses examples featuring UIButton etc.)
I'm out of ideas. It's obvious that the connection is not formed, presumably because the objects are instantiated in an order other than 'controller class first, IBOutlets later', but how can I force this connection?
#IBOutlet weak var sceneController: NSArrayController!
The weak keyword is your problem. If, after the system finishes decoding your nib, nothing else references the NSArrayController, then the system will immediately set the outlet to nil and deallocate the NSArrayController.
Try removing the weak keyword from your outlets.
UPDATE
Add this code:
#IBOutlet var sceneController: NSArrayController! {
didSet {
NSLog("sceneController set to %#", sceneController);
}
}
What's the output? Put a breakpoint on the NSLog. What's the stack trace when it's hit? Is it hit repeatedly?
I had a similar problem
I have a button that takes me from one view controller to another. Inside the event handler for this button I was trying to set values for some of the GUI components in the second view. Unfortunately, I was getting a nil value for those components.
The solution was to wait until the button handler exited then make changes to the GUI components in the second view (try the viewDidLoad() function on the second view controller). Presumably those GUI components were not allocated until I left the handler and switched views.
yuen helbig
I had the issue when I was configuring the corner radius of the buttons in viewWillAppear. Moving this part to viewDidLoad solved the issue.

why delegates should be unsafe_unretained and not weak?

I added ARC to an app I'm working on. Unfortunately, it crashes. I found that the automatic script which updates all apps to ARC gave __unsafe_unretained qualifier to all id< protocolName> type.
Why isn't it a weak type? I have deployed the app and all its sub-projects to iOS 5, and therefore I do have weak qualifiers.
My main problem is if I declare those delegates as strong, I'll have a retain-cycle. If I do not, the next time I call them they will be zombies. I checked and before my app crash, the delegate is NSZombie.
What is the cause of this crash and how can it be prevented?
The qualifiers __unsafe_unretained and week have quite a few things in common. They both won't increase the retain count for example. If for example a view controller holds an __unsafe_unretained IBOutlet to a UIView and you remove that very UIView from the view hierarchy, then you (given that you don't retain the view anywhere else) will decrease the retain count and most likely dealloc the UIView. The pointer however will still point to that location and is left dangling. Not nice but also not problematic if you know what happened. Weak properties help you avoiding dangling pointers by nullifying the property when the object gets to a retain count of 0.
Now, if your app crashes or the properties show up as zombies, then they are being released - by whichever class though.
One statement that is not entirely correct is that if you retain the property instead, you'll create a retain cycle. There is the possibility of creating retain cycles though but it really depends on your implementation, not just the property declaration. When you retain an object, you take ownership and until you're done with that object, prevent it from being deallocated by increasing its retain count. If your delegate gets released already while you hold a weak pointer, you won't prevent it from being released. I am assuming you deal with modal view controllers here - UIPopoverController to be precise (just a guess).
You should use instruments and look at the lifecycle of your object and see who retains/releases it. It could be helpful to know. Otherwise, you could paste some code and maybe there will be a nice person here to help you find the issue.
cheers
Ronny
Took some time but i solved it:
I deployed the .xcodeproj projects to iOS 5, but the targets were left in iOS 4.3 deployment. When i fixed it (it's in the 'build settings' for each target) - i could change all '__unsafe_unretained' to '__weak', and all 'unsafe_unretained' to 'weak'.
To avoid retain cycle those delegates should be weak, and they won't be zombies anymore (because they are weak and not unsafe_unretained), and the app won't crash anymore.
If i was still using iOS4.3-, and there isn't unsafe_unretained qualifer, i should only assign nil to those delegates after i don't need them anymore.

Call initWithCoder explicitly or ViewDidAppear equivalent in UIView?

In a UIView I have a nav button with an IBAction & method in the top-level view controller.
In the IBAction code, I flip a boolean so that when execution returns to the UIView, there's some new setup prior to drawRect: repainting the view.
If all this were in the ViewController, I could put the new setup code in something like ViewDidAppear so it executes each time the button is pressed. However, there's no such method at the UIView level. There is initWithCoder, but this only seems to be executed once (when the storyboard/nib loads).
So my question is - either, is there a way to call the initiWithCoder method explicitly from my IBAction at the VC level (I've tried [self initWithCoder:nil] but the breakpoint at the UIView level doesn't trigger) or is there a method that runs when execution returns to the UIView level, a la ViewDidAppear?
Thanks
Image of goal:
Unless you really know what you're doing (I mean really know), don't call -initWithCoder: yourself. You're meant to implement it just as you implement -drawRect: and let the system call it. If you ever find yourself calling something like this directly and you can't explain the deep technical reasons why there's no other way, then it's the wrong approach. Read and follow the documentation (not just the method's doc) to make sure you understand whatever method you're using. It'll tell you.
That said, what you're wondering is if there's a point in a view's lifecycle where you can "do something" (check a BOOL and perform some work if YES/NO) any time the view "appears". The answer is yes, and -willMoveToSuperview "can" work.
BUT
That's the "wrong" approach, IMO. The BOOL property ('draw a twiddle next time I'm asked to draw) can and probably should live in the UIView, but its state should be set in its controller since this is specific to your app. Views are supposed to be (highly) reusable; controllers are supposed to implement your app's specific logic and drive the views according to the model state and user (or system) actions.
So: when you want to enable the "draw a twiddle" operation, your view controller should set the view instance's drawTwiddle flag then probably flag the view for drawing. Your view will then have -drawRect: called at some point you shouldn't try to control and, when it does, it sees that self.drawTwiddle == YES and draws the twiddle along with whatever other drawing it does.
At that point, you might be tempted to have the view set its own drawTwiddle flag to NO since the behavior is intended to fire once. Don't do this. BEWARE: Other user actions or system events may call -drawRect: at any time so the twiddle may not actually be seen by the user (it may appear and disappear faster than is visible). 'So', the right thing to do is to make the controller (via some direct action, system event, or timer) responsible for setting and unsetting the drawTwiddle flag, then flagging the view for redisplay.
Adding
It's also unusual to put an IBOutlet or an IBAction in a UIView. Most of the time, unless you're creating some compound control whose parts aren't intended to be accessed and managed individually, your architecture is clearer (and more closely follows the spirit of the MVC design pattern) by letting the controller manage/own the outlets and actions.

replaceSubview:with and ARC is releasing old view

I have an issue when using NSView's replaceSubview:with: method to swap out different views. The old view is released when the method is called, the docs state,
This method causes oldView to be released; if you plan to reuse it, be
sure to retain it before sending this message and to release it as
appropriate when adding it as a subview of another NSView.
However, when using automatic reference counting (ARC) retain messages cannot be sent. Do the docs need to be updated, how can I use this method with ARC?
The views I am swapping exist all in the same nib and I do not have different view controllers. What is the preferred way of swapping out views and storing them for later use?
First store the old view for later use in a strong variable and then swap it out should prevent it from being released.

Resources