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.
Related
How can I change this line of code [NgnAVSession releaseSession: &audioSession]; so that I will no longer get this error:
Passing address of non-local object to __autoreleasing parameter for
write-back
this is the whole method
- (void)viewWillDisappear:(BOOL)animated
{
[NgnAVSession releaseSession: &audioSession];
[UIDevice currentDevice].proximityMonitoringEnabled = NO;
}
Here is the declaration of releaseSession
Header
+(void) releaseSession: (NgnAVSession**) session;
Implementation
+(void) releaseSession: (NgnAVSession**) session{
#synchronized (kSessions){
if (session && *session){
if([(*session) retainCount] == 1){
[kSessions removeObjectForKey:[*session getIdAsNumber]];
}
else {
[(*session) release];
}
*session = nil;
}
}
}
You are using a very old library. Try getting a newer version.
Then read up how Cocoa use NSError*. You really need to do this, because otherwise you cannot possibly understand what's going on.
Long story short: The compiler assumes that you pass the address of an autoreleasing variable. If needed it can turn a local variable into an autoreleasing one. That cannot be done with a non-local variable.
What these guys are doing is just wrong, wrong, wrong, wrong, wrong. If they want to keep track of all sessions without counting references, the easiest way is to create a wrapper object holding a weak reference, putting the wrapper objects into the array, and in the dealloc method you can remove the object from the array.
I'd suggest that you throw away their releaseSession and do exactly what I said before.
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.
I have found some problems when saving NSManagedObjectContext inside NSOperation with turned on ARC. Without ARC everything was fine before. It is always gives EXC_BAD_ACCESS during saving. The code looks like this:
//on the main thread
-(void)someFunc
{
array = ... //fetching an array of entities from a core data
for(SomeEntity * obj in array)
{
NSSomeOperation * op = [[NSSomeOperation alloc] initWithValue:[obj someField]];
//start an operation
}
}
//NSSomeOperation implementation
//...
- (void)main {
//some code
NSError * error = nil;
[mainContext lock];
if (![mainContext save:&error]) { //<--- HERE EXC_BAD_ACCESS
//process error
}
[mainContext unlock];
//some code
}
//...
Using of [mainContext setRetainsRegisteredObjects:YES] and objectWithID don't resolve this issue.
EXC_BAD_ACCESS (code=1)
EXC_BAD_ACCESS (code=13)
-[__NSCFType contextDidSave:]: unrecognized selector sent to instance 0x7fc5c505d940
An observer of NSManagedObjectContextDidSaveNotification illegally threw an exception.
Objects saved = {
inserted = "{(\n)}";
updated = "{(\n <SomeEntity: 0x7fc5c55b6220> (entity: SomeEntity; id: 0x7fc5c5052b20 ... )}"; }
and exception = -[__NSCFType contextDidSave:]: unrecognized selector sent to instance 0x7fc5c505d940 with userInfo = (null)
I use a separate managed object context and fetch my managed objects inside this NSOperation.
Maybe it is something related to Core Data bugs or ARC? Maybe ARC cleans some of objects, that must be saved?
Because, without ARC everything was fine, all worked. When I turned on ARC - EXC_BAD_ACCESS.
Does anyone know why it occurs?
Maybe ARC deallocates some object that receives NSManagedObjectContextDidSaveNotification and this causes the exception?
I had something similar, and fixed it by making sure to removeObserver: before the object gets deallocated.
Note that the CoreData exception actually hides the notification center exception, so you don't get to see it.
I can't undarstand why my code is crashes:
<__NSCFSet: 0x74957b0> was mutated while being enumerated
Previously I read simular topics but their problem was that the code calls in different threads. My code always calls in Thread 1.
It crashes times to time.
Here is a code where it happens:
- (void)processReceivedResponse:(JTResponse *)aResponse {
NSParameterAssert(aResponse);
id <JTRequestDelegate> delegate = [self processResponseWithReceiver:aResponse];
if (delegate == nil) {
for (JTObserver *someObserver in observers) {
if (someObserver.requestType == aResponse.type &&
![someObserver.delegate isEqual:delegate]) {
[someObserver.delegate didReceiveResponse:aResponse];
}
}
}
}
The error you're getting is caused by something changing the "observers" set while you're looping through it.
It's hard to tell what that might be from just the snippet you posted. Is something in the delegate you're invoking on someObserver changing the "observers" set?
You could try simply copying the observers set and looping over the copy:
for (JTObserver *someObserver in [[observers copy] autorelease]) {
...
}
If you're using ARC (automatic reference counting), you don't need to use autorelease.