The Xcode 4 static analyzer flags this method as a having an over-released return value when that does not seem to be the case.
- (id)copyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithURL:self.url postString:self.postString];
}
There is an arrow pointing from the return keyword to the expression following it, and another from that expression to the analyzer warning. Here is the static analysis:
Method returns an Objective-C object with a +1 retain count
Object sent -autorelease message
Object returned to caller as an owning reference (single retain count transferred to caller)
Object returned to caller with a +0 (non-owning) retain count
Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected
Is the static analyzer incorrect or is there something wrong with this code?
By request, the -initWithURL:postString: method:
- (id)initWithURL:(NSURL *)u postString:(NSString *)p
{
if ( (self = [super init]) )
{
self.url = u;
self.postString = p;
}
return self;
}
I continue to get this warning even after cleaning and rebuilding the project.
UPDATE: The Xcode static analyzer no longer flags this as an issue after upgrading to Xcode 4.2.
That's a bug in Xcode. The code is alright.
Related
The following code is from the LazyTableImages sample code provided by Apple (source here).
In their completion block they have a reference to self which should cause a retain cycle... But I don't get a warning for this in Xcode whereas in similar code of mine I would.
Is this correct?
Perhaps I'm missing a subtlety of this.
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.appRecord = appRecord;
[iconDownloader setCompletionHandler:^{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// Display the newly loaded image
cell.imageView.image = appRecord.appIcon;
// Remove the IconDownloader from the in progress list.
// This will result in it being deallocated.
[self.imageDownloadsInProgress removeObjectForKey:indexPath];
}];
[self.imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
}
}
The retain cycle that you think you are seeing is because the object holds the the downloader in a dictionary.
It's true that there is a strong reference to self in the block, but, as long as the completion handler is always run, the downloader will be removed from the dictionary. And eventually this dictionary will be empty, which means there will be no objects holding on to self, and thus no retain cycle.
self doesn't have a strong pointer to iconDownloader. It's created and scoped just to this method:
IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
If iconDownloader was a strong property (self.iconDownloader) then Xcode would detect a strong reference cycle.
Capturing self itself is no retain cycle. It is a single reference. One reference cannot build a cycle. The usual antipattern is, that additionale a reference to the block is stored in a strong property of self. Than there are two references building a cycle.
There's no warning because the compiler isn't yet capable of detecting all possible retain cycles.
For example:
- (void)foo
{
_block = ^ { [self done]; }; // Warning: Possible retain cycle
DSGenericBlock foo = ^ { [self done] };
_block = foo; // No warning.
}
If you were to assign the block directly to an instance variable of "self", you would get the "possible retain cycle" warning. Instead, the block is assigned to another object, which is then retained by self, so the compiler does not detect the cycle (even though the cycle does exist).
This question already has an answer here:
Self managing/releasing objects reported as "Potential leak of object" under Xcode -> Product -> Analyse
(1 answer)
Closed 9 years ago.
I am almost certain that I don't have a leak in this code, yet the Xcode analyzer reports that there is a "potential" leak (Xcode 4.6.1).
+ (MySHA1hash *)sha1HashWithHashBytes:(unsigned char *)hash length:(unsigned int)length;
{
return [[[MySHA1hash alloc] initWithHashBytes:hash length:length] autorelease];
}
If the problem is that Xcode is reporting a false positive, I would like to figure out how to structure the code in a way to silence the warning.
It is also the possible that I am leaking in a way I don't understand, but If someone can see how I am actually leaking I would be glad to get that feedback as well.
This must have something to do with the init functions I call, because if I simply replace initWithHashBytes with init, then the leak is no longer reported. To that end I also include the body of initWithHashBytes.
- (id)initWithHashBytes:(unsigned char *)hash length:(unsigned int)length
{
if (hash != nil && length <= SHA_DIGEST_LENGTH) {
NSData *data = [NSData dataWithBytes:hash length:length];
self = [self initWithHash:data];
}
else {
self = nil;
}
return self;
}
- (id)initWithHash:(NSData *)hash
{
if ([hash length] <= SHA_DIGEST_LENGTH && (self = [super init]) != nil) {
finished = YES;
[hash getBytes:sha_Result];
hashValue = [NSNumber numberWithInt:[hash hash]];
}
else {
self = nil;
}
return self;
}
The line
self = nil;
in initWithHashBytes: (and initWithHash:) is the issue. You are allocating an object, but if you return nil from from initWithHashBytes:, that object will be leaked, because you'll call autorelease on nil rather than on the object you allocated.
Release self before you return nil and all should be good.
In this particular case there was obviously an error that needed to be fixed, but I have seen at times a need to suppress warnings that are completely understood to be non problems (i.e. a leak reported that is not actually a leak).
This is what I expected to need to do here, but it turned out that there was an actual leak. So I am glad it got fixed. I immediately found another problem that was a clear an unmistakable "false positive" (I know that the error is reported as a "potential leak" so in reality it isn't a false positive, but it doesn't mean I want to see it in the report every time I run the analyzer).
Because of this I still had the question of how to suppress these warnings. It turns out you can easily wrap code that you want the analyzer to bypass in a ifdef check for __clang_analyzer.
#ifndef __clang_analyzer__
... code you want to ignore ...
#endif
There was a very good write up on this here.
You are missing a [self release] before self = nil.
The object you get from alloc has a reference count of +1 which needs to be balanced by a call to release or autorelease. For the case where you return your actual object from sha1HashWithHashBytes:length: the autorelease in that class method takes care of everything.
For the case you return nil your init method is the last one that has a reference to that allocated object so it has to release it.
Problem:
I have an __unsafe_unretained id pointer that points to an already released object. So far so good, as long as I do not "use" the pointer at all (in particular, I do not call any method through the pointer). However, when I try to return its value from a method, it crashes, even if I have explicitly specified that the return value has the type __unsafe_unretained id. Why is that? I thought if I use __unsafe_unretained, it would not call methods like retain / release / autorelease at all? I thought I can use __unsafe_unretained id pretty much as if it is a void* (meaning that it only does simple, native assignments)?
Environment:
Developing on Xcode 4.4.1
Using iOS SDK 5.1
ARC is enabled
Running on iPhone 4.3 / 5.0 / 5.1 Simulator or iPhone 4.3 Device
Crashes on both Debug and Release builds
Source Code:
// Declare my class with 1 member.
#interface MyClass : NSObject
{
__unsafe_unretained id m_MyMember;
}
#end
// **************************************************************************************************** //
// Implement my class.
#implementation MyClass
// Setter
-(void)SetMember:(__unsafe_unretained id)member
{
m_MyMember = member;
}
// Getter: by passing parameter by reference
-(void)GetMember1:(__unsafe_unretained id*)member
{
*member = m_MyMember; // No problem.
}
// Getter: by return value
-(__unsafe_unretained id)GetMember2
{
return m_MyMember; // Crashed in here!
}
#end
// **************************************************************************************************** //
//! Application entry point.
int main(int argc, char *argv[])
{
#autoreleasepool
{
{
// Create an object that dies immediately. deadObj is a dangling pointer.
__unsafe_unretained id deadObj = [[NSMutableString alloc] initWithFormat:#"%d", 12];
// Create my object.
MyClass* myObject = [[MyClass alloc] init];
// Assign my member.
[myObject SetMember:deadObj];
// Get back my member: by passing parameter by reference
__unsafe_unretained id unsafePointer1;
[myObject GetMember1:&unsafePointer1]; // No problem.
// Get back my member: by return value
__unsafe_unretained id unsafePointer2;
unsafePointer2 = [myObject GetMember2]; // Crashed in here!
int BreakpointHere = 0;
}
}
}
Call Stack (iPhone 4.3 Simulator/iOS 4.3 Device):
#0 0x011db09b in objc_msgSend ()
#1 0x00106712 in __arclite_objc_retainAutoreleaseReturnValue at /SourceCache/arclite_host/arclite-29.1/source/arclite.m:259
#2 0x00001fec in -[MyClass GetMember2] at /Users/user/SourceCode/main.m:28
#3 0x00002147 in main at /Users/user/SourceCode/main.m:56
Call Stack (iPhone 5.0/5.1 Simulator):
#0 0x014f6d25 in objc_retain ()
#1 0x014f7fe3 in objc_retainAutoreleaseReturnValue ()
#2 0x00001fec in -[MyClass GetMember2] at /Users/user/SourceCode/main.m:28
#3 0x00002147 in main at /Users/user/SourceCode/main.m:56
I think the behavior can be explained with the following information 3.2.3. Unretained return values in the Automatic Reference Counting documentation:
A method or function which returns a retainable object type but does
not return a retained value must ensure that the object is still valid
across the return boundary.
When returning from such a function or method, ARC retains the value
at the point of evaluation of the return statement, then leaves all
local scopes, and then balances out the retain while ensuring that the
value lives across the call boundary. In the worst case, this may
involve an autorelease, but callers must not assume that the value is
actually in the autorelease pool.
Your function GetMember2 returns an id, which is a retainable object type. Therefore the ARC compiler adds retain/autorelease calls to make sure that the returned object is still valid when the function returns. This crashes because m_MyMember does not point to a valid object.
Declaring the return type as (__unsafe_unretained id) does not change this behavior, in fact I assume that the __unsafe_unretained is ignored here.
I recently changed a bit of code using Archiver to replace the object being decoded with another if the object was in a shared list. My app is using ARC.
- (id)awakeAfterUsingCoder:(NSCoder*)decoder {
Player* player = [[Team getCurrentTeam] getPlayerWithId: self.id];
// If the player is in the current team then use that instance instead of this one.
if (player != nil) {
return player;
} else {
return self;
}
}
The code worked great until I added the awakeAfterUsingCoder. Now I am getting malloc: error for object 0x7567430: pointer being freed was not allocated. It would appear that the object being replaced is being released but it was never retained? Since I'm using ARC I'm not sure what I might do to resolve this.
The following code implements an NSProxy subclass which forwards methods to an NSNumber instance.
However when calling [nsproxy floatValue] I get 0.0 under GCC 4.2.
Under LLVM-Clang I get the correct answer 42.0.
Any idea what is going on?
(by the way this is running under Garbage Collection)
-(id) init;
{
_result = [NSNumber numberWithFloat:42.0];
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [[_result class] instanceMethodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:_result];
[anInvocation invoke];
return;
}
Your proxy class doesn't declare the message signature for -floatValue. Since it returns a floating point number, that can be a problem. Because you haven't declared it anywhere and presumably aren't casting your proxy object to its' represented class the compiler has to guess at the method signature. In that case the GCC compiler guesses that the message will return an id pointer.
In Objective-C, depending on the signature of the message and the architecture of the machine, different functions get used for processing the message and its' return values. On x86 machines messages that return a floating point value are called through objc_msgSend_fpret while functions that return void and id use objc_msgSend.
Since the GCC compiler is assuming that the return value is an id it uses the latter function and incorrectly handles the result. That Clang is able to handle this properly is interesting but I would hesitate to rely on that behavior. It would be better to declare a category on your proxy for any methods that you'll be forwarding. That also has the benefit of removing the warning that was being generated for the line of code calling the floatValue method.
#interface Foo (ProxyMethods)
- (float)floatValue;
#end
Any init method should call [super init]; to prevent unexpected behaviour. As so:
- (id)init {
self = [super init];
if (self) {
// Your init code
}
return self;
}