Why cannot XCode static analyzer detect un-released retained properties? - iphone

I have this retained property declared like this:
#property (nonatomic, retain) NSMutableDictionary *codes;
then I synthesize this:
#synthesize codes;
I use the property like this:
self.codes = [NSMutableDictionary dictionary];
Then, I forget to say [codes release]; in my dealloc.
When I run Analyzer in XCode 4.3.2, this is not shown as an issue. My base SDK is iOS 5.1 and my compiler is Apple LLVM compiler 3.1
Why doesn't analyzer pick this up?

I imagine it's because the analyzer can't reliably detect retain/release issues across method/library boundaries.
You could conceivably pass ownership of your codes array to some external method or library which will release it later on for you. This would be bad practice because the receiving method should just retain it if it needs it, but I've seen this kind of thing done by inexperienced developers.
So you might see this in your class somewhere:
[SomeAPI takeThisArrayAndReleaseItLater:codes];
The analyzer has no way to know that your class is no longer responsible for releasing the array. To give you a warning would be incorrect, despite the fact that you are not following good memory management practices.
The analyzer is very good at only warning on real issues. I don't think I've ever seen a false-positive outside of betas builds, which is a good thing.

If you havent change anything from the configuration, whenver you target ios5+ you will automatically be using ARC (Automatic Reference Counting) which doesnt require you to release or retain.
The most disruptive change in iOS 5 is the addition of Automatic
Reference Counting, or ARC for short. ARC is a feature of the new LLVM
3.0 compiler and it completely does away with the manual memory management that all iOS developers love to hate.
This is a post by iOS Tutorial Team member Matthijs Hollemans, an experienced iOS developer and designer.

Related

App crashes after Xcode upgrade to 4.5. Assigning retained object to unsafe_unretained variable

In my class I have a dispatch_queue_t property declared like this:
#property (nonatomic, assign) dispatch_queue_t queue;
Then in my init method I do:
- (id)initWithServerUrls: (NSString*)serverUrls
{
if (self = [super init])
{
_queue = dispatch_queue_create("com.xxx.my_send_queue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
In Xcode 4.4.1 it worked and did not cause any problems (the app tested + in the appstore).
Now after I upgraded to Xcode 4.5 the app crashes with EXC_BAD_ACCESS and Xcode gives me a warning on that line saying:
Assigning retained object to unsafe_unretained variable; object will be released after assignment
Apple updated the compiler in Xcode 4.5 from LLVM 4.0 to LLVM 4.1 but I have no clue why my code is crashing right now.
I stepped through the code and the crash happens just after that line.
Do you have any ideas what can be wrong and how can I fix it?
SOLUTION:
I managed to get it working with both SDKs. I just added:
#if OS_OBJECT_USE_OBJC
#property (nonatomic, strong) dispatch_queue_t queue; // this is for Xcode 4.5 with LLVM 4.1 and iOS 6 SDK
#else
#property (nonatomic, assign) dispatch_queue_t queue; // this is for older Xcodes with older SDKs
#endif
Hope someone finds it useful
First, if your target platform is 5+, then I strongly recommend building with the iOS 5 SDK. Building with a later SDK and setting the "target" can work, but has lots of problems (not the least of which is that you get no compiler help to find places that you've used unsupported methods). So answer 1: You need iOS 5, build against iOS 5 and this shouldn't matter.
In iOS 6, dispatch_queue_t is an ObjC object. This is a great improvement. It means you can just create strong properties for it and ARC will take care of the rest. If you target iOS 6, this should just work.
If you need to build the same code for iOS 5 and iOS 6, then you need to know which is which so that you can put in the memory management when you need it and leave it out when you don't. The correct test to use is #if OS_OBJECT_USE_OBJC. Remember, this is a compile-time check. It's only applicable for dealing with code you want to write against different SDKs. For a given SDK, the behavior will aways be one way or the other.
Regarding the "unsafe_unretained" versus "assign" confusion: They are the same thing in this case. "assign" only applies to non-objects. "unsafe_unretained" is what "assign" is converted to when applied to objects. And in iOS6, dispatch_queue_t is an object.
One more workaround, particularly if you really do want to keep the old memory management code while building with the iOS 6 SDK. You can pass -DOS_OBJECT_USE_OBJC=0 to the compiler. This will opt-out of the new model. But I would recommend this as a last resort. For details, see os/object.h in the SDK. (Cmd-Shift-O, object.h)

Xcode Analyze 69 potential memory leaks

I'm making my first app and it's really confusing.
I am using ARC, and probably ALL of my potential leaks say:
"Object leaked: object allocated and stored in 'point' is not referenced later in this execution path and has a retain count of +1"
They are on almost any object I create using [[alloc] init].
Any suggestions about how to handle those? The app works fine, though.
Thanks.
Are you sure your project is actually set to ARC? Those types of analyzer warnings sound like it isn't. Is this a new project where ARC was automatically turned on for you or did you do the conversion for it yourself?
There isn't a way to actually tell the analyzer that you are ARC or aren't. It will just pick that up from the settings automatically.

app runs fine with debug build, but crash on release build, what could be the possible reasons?

I have Xcode 4.3.1, iOS 5.1, and have ARC turned on for building my app.
Now the app runs fine in debug build, but crash on release build. What could be the possible reason for the difference? I purely rely on ARC for the resource management. I looked at the crash log, it's indicating that the memory that was referencing was released already. What'll be the common pitfalls that could cause the problem on retail build, when using ARC?
The following is what I got from crash log
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x6f636552
Crashed Thread: 0
EDIT
The app's deployment target is iOS 5.0. I do use internet connections, the current crash happens on the time when "rendering" the data returned from web service in order to show on a UITableViewController. The whole app is using ARC, except a few source files from 3rd party for which I have ARC turned off.
Whenever this happens to me it seems to be because release builds are more aggressive about clearing up weak references. If you mistakenly assign something to a weak property (for example, if you're adding subviews that you will also hold weak references to) before you have any strong reference to it, this can work on debug and fail on release. For example (pseudocode)
#property (weak) UILabel * label;
...
self.label = [[UILabel alloc] init];
[self.view addSubview:self.label];
...
self.label.text = #"hello";
I've seen this cause bad access crashes on release builds and go unnoticed on debug.
I may not have the answer but I'm going to list a few hunches for you to try:
Make sure you are not passing objects into methods without a "handle" on it on your side. And example would be passing a handler class instance to a method that expects a delegate. The method doesn't retain that instance and so it gets released before it even calls the method.
Check your pre-compiler macros that they are safe for both DEBUG & RELEASE builds. A good example is having an if statement over a macro that is removed in release builds and the if statement doesn't cover it with curly braces.
If you depend on compiler definitions to enable/disable certain parts of your code (through the use of #if conditions) make sure the needed ones are set in your build configuration.
If I can think of more, I'll try to add them.
Do you have a different target for release and debug? Check whether all the files are correctly referenced for the release target.
In our case, a category on UIButton was not seen by the release target. An ad-Hoc build went just fine, until someone invoked a method implemented by that category. Since we did not store an archive from the Ad-Hoc build, there was no way to debug a crash. (lesson learned)
Not sure whether it is listed as EXC_BAD_ACCESS in a crash log, but could help someone to identify their release-specific crash.

How do I replace weak references when using ARC and targeting iOS 4.0?

I've begun developing my first iOS app with Xcode 4.2, and was targeting iOS 5.0 with a "utility application" template (the one that comes with a FlipsideViewController).
I read that since ARC is a compile-time feature, it should be compatible with iOS 4 as well, so I attempted to target my app to 4.3, and try compiling it. When I do so, I get this error:
FlipsideViewController.m: error: Automatic Reference Counting Issue: The current deployment target does not support automated __weak references
It is referencing this line:
#synthesize delegate = _delegate;
That variable is declared as:
#property (weak, nonatomic) IBOutlet id <FlipsideViewControllerDelegate> delegate;
I understand that "weak references" are not supported in iOS 4, but I don't really understand why I would want to use a weak reference to begin with, nor can I figure out how I would rewrite things to avoid using it, while still taking advantage of ARC (after all, it's supposed to work with iOS 4 AND 5 right?)
To target the older OS, you can use unsafe_unretained instead of weak in your property declaration, and it should mostly work the same way. weak references nil themselves when their target goes away, but unsafe_unretained leaves open the possibility that the object you're linking to could turn into a dangling pointer when it is deallocated. The latter is the same behavior as if you had used assign as a property declaration in manual memory management.
You do this to avoid retain cycles, which I mention in my answer here. You don't want to have a strong pointer to something that might have a strong pointer back to the original object. Then nothing would get released properly.
If only using weak references for additional safety, manually call the new runtime functions if they're available and fallback to simple assignment on __unsafe_unretained variables if not.
ZWRCompatibility.h will simplify this somewhat.
Thanks to Mike Ash's compatibility library PLWeakCompatibilty, you can now simply use __weak on iOS 4.x, as well.
It's incredibly easy to configure and requires no additional consideration or effort over 5.x.

How to tell when object is sent release message?

I'm working through a somewhat tricky iPhone crash, and it looks like the culprit is an NSString being prematurely released. I've switched on NSZombiesEnabled and can see that the NSString is a zombie at the time of the crash. However, I can't determine when the object is being released/dealloced--I've combed through my code looking for release messages being sent to this object and have set breakpoints at these spots, but they're not being hit.
I assume this may be a threading or autorelease issue given it's intermittent nature, but is there any way to hook into the objective-c runtime via the Xcode debugger to tell the exact point where an object is being released? Or is there a better way to diagnose this issue?
If you can reproduce the crash in the simulator, you may wish to look into using the malloc_history tool. (It has a man-page.) You need to set some environment variables: I normally set them via the "Edit Active Executable" screen in the Arguments pane, and then use the check-boxes there to enable/disable them. Make sure you disable them before debugging on the device; if enabled the program will try to write to /tmp which the sandbox doesn't allow.
I find this tool combined with NSZombie lets me track down alloc/premature-release/access-after-dealloc errors. Once NSZombie reports access to a deallocated object you can use malloc_history to work out when the object was allocated. This normally sets me on the path to working out where the problem is.
Another tool I've found invaluable is clang from the LLVM project. It's still in development, but they regularly produce binaries for MacOS-X that seem pretty stable to me. In particular, it understands the Cocoa memory-management policy. Using it is as simple as:
% cd ${DIRECTORY_CONTAINING_XCODE_PROJECT}
% xcodebuild clean
% scan-build -V xcodebuild
This will do a full build of your project and produce a report listing any obvious errors (including reference-counting screw-ups) that the tool finds.
I might not be thinking straight, but have you considered adding a release and dealloc onto your class
- (void) release
{
NSLog(#"Releasing");
[super release];
}
- (void) dealloc
{
NSLog(#"Deallocating");
[super dealloc];
}
Incorporating Ben Gotow's comment to use an obj-c category, you end up with this:
#interface NSString (release)
-(void) release;
#end
#implementation NSString (release)
-(void) release
{
NSLog(#"NSString Released!");
[super release];
}
#end
you could tell the objc provider of dtrace to trigger your probe whenever -[NSString release] is called, but this will involve a little nasty hackery. NSStrings aren't actually NSStrings but are all subclasses, because of the way the class is implemented as a class cluster. Now, that's not going to get in our way; what will is that NSString doesn't have its own -release :-). You can provide your own in a category, though.
Alternatively, if it's easy for you to tell which instance of NSString is going to break, you could just set a conditional breakpoint on -[NSObject dealloc] with self==myInstance.
Another way to do this. Make sure to turn NSZombie on so it reports the memory address of the object that is getting the extra release. Then Run with Performance Tool->Object Allocations. This will bring up instruments. Look at the Console log as provided by Xcode organizer. Once you get the crash lookup the memory address in instruments. You will see the entire history of mallocs/frees on that object, as well as links straight into your code.
Could you implement dealloc() and put a breakpoint in there? Looking at the stack trace from that point should tell you where and how it's being released.