why delegates should be unsafe_unretained and not weak? - delegates

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.

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.

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.

How to release everything thats retained by a viewController?

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.

Unpredictable EXC_BAD_ACCESS and objc_msgSend_vtable5

I was testing my application and suddenly it had EXC_BAD_ACCESS. Now this has become a semi-regular thing, happening on some builds and not others. It also spits out lots of errors in the debugger such as objc_msgSend_vtable5 What could cause such a weird issue like this?
Looks like it was my fault. To fix it all I needed to do was call retain.
I've recently had just this same error. The short version: it occurred because I had an object that had a delegate that had been released. Make sure you have control over the life cycle of any delegates you assign, be it via Interface Builder or otherwise.
The long version: the error occurs when the Objective-C runtime tries to send a message to an object that's been ultimately released and deallocated. This is different to sending a message to a nil object (a perfectly valid Objective-C operation). In these cases, the runtime knows that the object is nil and responds accordingly. Rather, in this case, Objective-C thinks it has a real, valid object to communicate with and sends a message to an address that contains no object.
In my case, I had an object of type NSTextView which was connected up to a delegate by Interface Builder. When a view or view controller is constructed with a NIB/XIB file, Cocoa hooks up all the child views as instructed, including hooking up any delegates that you might have.
My error was that I had created a subclass of NSView, made it an NSTextViewDelegate and then told Interface Builder to hook up a child NSTextView to that parent view. However, during the various operations I was conducting on the owning NSViewController, I had told Cocoa to remove the NSView from the view hierarchy and reinsert it later.
AppKit specifically tells you not to retain any IBOutlets you might have in your classes, but rather to keep track of these by simple assignment. This is because AppKit handles the memory management of these for you. Little did I know that by asking the view to leave the view hierarchy for a while, I had trigged AppKit to clean up that NSView. Since only AppKit had any retains on the view object itself, [pfft], off it went into the ether. Deallocated.
Since I had no direct control over the NSTextView's delegate, the NSTextView now pointed to a delegate that had been released, even though it had a valid parent view. The next time it tried to send a message to its delegate, the Objective-C runtime fell over.
So, make sure that the delegate is one that you own, or at least know will never get released at an inopportune moment.

NSLevelIndicator Not Updating

I've got an application with an NSLevelIndicator Object on it that's refusing to update.
I have a timer that's shoved off during init and updates the value of the NSLevelIndicator using its setIntValue method. Whilst the code executes without any exceptions, the NSLevelIndicator never visually updates. I have some other labels on the window that are updating through this timer, so I know that it is executing.
I've tried using all of the setTypeValue methods (String, straight value and double with appropriate variables being assigned in each). I even tried linking the "setStringValue" action through interface builder from the NSLevelIndicator to a label representation on the window to no avail. It still sits at its initial value (0).
I noticed that setIntValue (and all the other setTypeValue methods) are undocumented in Apple's documentation for NSLevelIndicator - so I'm wondering if I'm approaching this wrong.
Does anyone have any clue what the proper way to set an NSLevelIndicator's value from code is?
setIntValue should work, so it sounds like your IBOutlet for the NSLevelIndicator isn't set properly - most likely its value is nil.
This is probably due to your outlet not being connected in IB, as Johan Kool suggested.
One thing worth mentioning, however, is that IBOutlets don't yet have a valid value at the time your initializer is called - they're hooked up after the initializer returns and shouldn't be referenced until your instance receives the awakeFromNib message.
You mentioned your timer is set up from your initializer - if you happen to be passing your instance's NSLevelIndicator pointer to the timer as its userInfo parameter, the userInfo will have the wrong value (nil) since it isn't yet initialized when the timer is created.
Regardless of whether you use userInfo this way, anything that depends on IBOutlet values should be set up from within awakeFromNib rather than init.

Resources