UIImage setter NSInvalidArgumentException - objective-c

have a problem with object setters in a class.
I have the class GEOImage, where things like description, title etc. will be saved according
to an image.
#interface GEOImage : UIImage
{
NSString *title;
NSString *imgDescription;
NSString *latitude;
NSString *longitude;
NSDictionary *editInfo;
}
#property (nonatomic, copy) NSString *title, *imgDescription, *latitude, *longitude;
#property (nonatomic, copy) NSDictionary *editInfo;
#end
Now i try to store a description out of another class:
self.chosenImage.imgDescription = #"description";
where chosenImage is of type GEOImage.
But i get the error:
-[UIImage setTitle:]: unrecognized selector sent to instance 0x939d220
2011-12-05 10:59:40.621 GeoPG[511:17c03] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImage setTitle:]: unrecognized selector sent to instance 0x939d220'
If I'm looking in the debugger, the chosenImage is not NULL, and its been displayed correct in an image view.
Greets
s4lfish

We can safely infer that chosenImage is not nil; if it were nil, sending it a message would simply do nothing, not crash.
(Also, I'm assuming that you meant title rather than imgDescription in your usage sample, or that you implemented setImgDescription: to set the title in turn.)
There are two possibilities:
Dead object
You created the image you stored to chosenImage as a GEOImage, but then it died while you were holding on to it. Subsequently, a UIImage (as identified in the exception message) was created at the same address, so the pointer you still hold now points to a UIImage. You sent a message to it that only works on GEOImages, but it's only a UIImage, so it doesn't respond to the message, which is the exception.
The cause of an object dying while you're holding it is that either you didn't retain it somewhere where you should have, or you released it somewhere where you shouldn't have. Or possibly both.
Run your app under Instruments with the Zombies template. It will raise a flag when you hit this crash, and you can then investigate by clicking the button in that flag. Look at all of the Release and Autorelease events, starting from the end, to find the one that shouldn't be there; then, if the release itself is unwarranted, take it out, or if it should be balanced by a previous retain, put one of those in.
One possible cause of the crash is that you declared the chosenImage property as assign, but you should have declared it as retain/strong. If this is the problem, your Instruments findings will support it.
Long-term, you should convert to ARC, which eliminates 90% of the cases where this problem could happen.
You never created a GEOImage in the first place
Just because you declared that chosenImage will hold a pointer to a GEOImage doesn't mean it does. You can assign any object pointer there, and in many cases, the compiler doesn't know if it isn't actually a GEOImage.
(They introduced a feature called “related result types” in a future version of Clang that should make this much less likely.)
At a guess, I'd say you're doing something like this:
self.chosenImage = [GEOImage imageNamed:#"blah blah blah"];
or this:
self.chosenImage = [imagePicker takePicture];
There is no reason to expect takePicture to return a GEOImage (how should it know that's what you want?), and it's likely that +[UIImage imageNamed:] (assuming you simply inherit it) won't, either. Unless you create a GEOImage instance yourself, using alloc and an initializer, you cannot assume that any UIImage you get will be a GEOImage.
The solution is to make it easy to create a GEOImage from a UIImage (which will involve wrapping this method), and then do that.
Once you have a live (not dead) GEOImage (not UIImage) in your chosenImage property, it will work.

In fact, it was like Peter said: I never created an GEOImage. I now I create the GEOImage like this:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info valueForKey:#"UIImagePickerControllerOriginalImage"];
NSURL *imgURL = [info valueForKey:#"UIImagePickerControllerMediaURL"];
CGImageRef imageRef = [image CGImage];
[self createGEOImage:imageRef info:info imageURL:imgURL];
}
And the method called with the imageRef:
- (void)createGEOImage:(CGImageRef*)imageRef info:(NSDictionary*)info imageURL:(NSURL*)imgURL{
GEOImage *geoImage = [[GEOImage alloc]initWithCGImage:imageRef];
geoImage = info;
geoImage = imgURL;
}
Thanks for your help!

Related

Mac OS X, Objective-C crash with NSMutableArray

Environment: Mac OS 10.8.5, XCode 5.1.1
Problem: Crash in obj_msgsend on addObject message to a NSMutableArray
Disclaimer: I'm new to Objective-C, so this could an obvious mistake. But it's mysterious.
Details:
I've been able to prune the problem down to a small test case (thankfully), though the exact manifestation of the problem is different from the full application.
Here's the #interface:
#interface ObjCQueue : NSObject
+ (void) push: (NSString *)calEvent;
+ (NSString *) pop;
#end
Here's the Objective-C class implementation.
#import <Foundation/Foundation.h>
#include "ObjcQueue.h"
NSMutableArray *qArray;
#implementation ObjCQueue
{
}
+ (void) init
{
qArray = [[NSMutableArray alloc] init];
// NSLog(#"(init)qArray class is: %#\n", NSStringFromClass([qArray class]));
}
+ (void) push:(NSString *)calEvent
{
[qArray addObject:calEvent];
}
+ (NSString *) pop
{
// This will return nil if there's no first object
NSString *retEvent = [qArray objectAtIndex:0];
// Don't delete the front of the queue if nothing is there
if (retEvent != nil)
[qArray removeObjectAtIndex:0];
return retEvent;
}
#end
and main.m does this:
int main(int argc, const char * argv[])
{
#autoreleasepool {
[ObjCQueue init];
[ObjCQueue push:#"Pushed thing"];
NSLog(#"Popped: %#\n", [ObjCQueue pop]);
}
return 0;
}
For the moment, let's ignore the possibility that how I'm doing this is totally wrong (we'll get back to that).
If I run this code as-is, I get a crash in objc_msgSend called by the addObject message sent from [ObjCQueue push:]
The mystery part is, if I uncomment the NSLog call in [ObjCQueue init] everything runs just fine.
In the larger application, I see a different issue. The failure also occurred in the push method, except the run-time error I got said that addObject was an invalid selector. When I check the type of qArray in that case, it has a type of NSDictionary (that's from memory, it wasn't spelled exactly that way) instead of NSMutableArray. Also, in the larger application, adding the NSLog call in the init method makes everything run smoothly.
In this smaller example, the type of qArray always appears to be NSMutableArray.
In other answers to similar questions, the implication is that the object corresponding to qArray is getting overwritten, and/or released prematurely. I don't see how that could happen here, since it's global, and ObjCQueue only has class methods and no instance of it is created. [ObjCQueue init] is only called once.
One other bit of data: In this smaller case, qArray gets displayed differently depending where (in the debugger) it's displayed.
In init, in the case where it crashes, immediately after qArray gets its value, the debugger shows:
Printing description of qArray:
<__NSArrayM 0x10010a680>(
)
But in push, just before the addObject method is called, the debugger shows:
Printing description of qArray:
(NSMutableArray *) qArray = 0x000000010010a680
The value is the same, but the type is kinda sorta different (maybe). In the case with no crash, the display is identical in both cases (they're both the same as the first display)
This may not be the best way (or it may be a blatantly wrong way) to initialize qArray, and I can accept that. But why would the behavior change with the addition of the NSLog call?
Any help/insights will be appreciated.
-Eric
P.S. Here's the XCode project: Bug Test
The problem is because ARC is releasing qArray before you called push so you're calling on an object that is already released. A good solution to this problem would be to either change your class to an actual instance, or create a singleton so that ARC knows to retain the array rather than just releasing it right after you init.

NSImage memory leak

First of all: I already searched on google and SO for solutions - none worked.
I've got an application which loads the artwork of the current iTunes track and displays it; this is stored in a NSImage instance, among some other variables, in a class:
#interface infoBundle : NSObject
#property (strong) NSImage *track_artwork;
#property (weak) NSString *track_title;
#property (weak) NSString *track_album;
#property (weak) NSString *track_artist;
#end
Then, an instance of this class is created:
-(infoBundle*)returnInfoBundle {
infoBundle* tmpBundle = [[infoBundle alloc]init];
tmpBundle.track_artwork = [[NSImage alloc]initWithData:[(iTunesArtwork *)[[[iTunes currentTrack] artworks] objectAtIndex:0] rawData]];
[...]
return tmpBundle;
}
And later used:
-(void)iTunesDidChange {
infoBundle* tmpBundle = [self returnInfoBundle];
[...]
[imageView setImage:tmpBundle.track_artwork];
}
That's eating up ~2MB (Cover size, I'd guess) per call of iTunesDidChange.
I already tried:
[tmpBundle autorelease];
[tmpBundle release];
[tmpBundle dealloc];
tmpBundle = nil;
and, after that didn't help:
- Enabling ARC.
=> Why is this eating up memory, although the object (tmpbundle) should get removed?
=> How may I achieve leak-less NSImage usage?
Thanks for any tips/suggestions/solutions :)
Issue
You will have a memory leak if you create your object on your method and not release it inside that method or you have to reference it when you pass it as a parameter by reference : Passing arguments by value or by reference in objective C
Your problem is that you are creating an instance of infoBundle two times, and when you are initializing another instance of it, you are leaving the first one without reference, so it remains in memory, and without connection to remove it (memory leak).
Solution
To make your things easier you should create an instance of your object
#implementation
{
infoBundle* tmpBundle;
}
Use it where ever you need it
-(infoBundle*)returnInfoBundle
{
tmpBundle = [[infoBundle alloc]init];
tmpBundle.track_artwork = [[NSImage alloc]initWithData:[(iTunesArtwork *)[[[iTunes currentTrack] artworks] objectAtIndex:0] rawData]];
[...]
return tmpBundle;
}
-(void)iTunesDidChange
{
tmpBundle = [self returnInfoBundle];
[...]
[imageView setImage:tmpBundle.track_artwork];
}
And when you are finished with that object dealloc will automatically release it if you add it to dealloc method:
- (void) dealloc
{
[tmpBundle release];
tmpBundle = nil;
}
Hope it helps! :)
Just modifiy this line :-
infoBundle* tmpBundle = [[[infoBundle alloc]init]autorelease];
I can’t tell from your code what you are doing in [imageView setImage:tmpbundle.track_artwork]; but you may be having the same problem I had.
I was using
self.imageToDisplay = [UIImage imageNamed:pictFileName];
and kept getting leaks. I switched to
self.imageToDisplay = [UIImage imageWithContentsOfFile:pictFile];
and they went away.
According to the documentation for imageNamed,
This method looks in the system caches for an image object with the
specified name and returns that object if it exists… If you have an
image file that will only be displayed once and wish to ensure that it
does not get added to the system’s cache, you should instead create
your image using imageWithContentsOfFile:. This will keep your
single-use image out of the system image cache, potentially improving
the memory use characteristics of your app.
It sounds like you have either the same or a similar issue.

Why does my NSArray get deallocated?

I'm trying to understand Automatic Reference Counting, as I come from a high-level programming language (Python) and I'm working on a project which use this feature of Objective-C. I often get problems with ARC deallocating objects which I need later, but now I got a concrete example for which I hope I'll get an explanation.
- (void) animateGun:(UIImageView *)gun withFilmStrip:(UIImage *)filmstrip{
NSMutableArray *frames = [[NSMutableArray alloc] init];
NSInteger framesno = filmstrip.size.width / gun_width;
for (int x=0; x<framesno; x++){
CGImageRef cFrame = CGImageCreateWithImageInRect(filmstrip.CGImage, CGRectMake(x * gun_width, 0, gun_width, gun_height));
[frames addObject:[UIImage imageWithCGImage:cFrame]];
CGImageRelease(cFrame);
}
gun.image = [frames objectAtIndex:0];
gun.animationImages = frames;
gun.animationDuration = .8;
gun.animationRepeatCount = 1;
[gun startAnimating];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(arc4random() % 300)/100 * NSEC_PER_SEC), dispatch_get_current_queue(),^{
[self animateGun:leftGun withFilmStrip:[self getFilmStripForAction:gunShoot andTeam:nil withWeapon:nil]];
});
}
The idea behind this snippet of code is simple: I have a (UIImageView*)gun which I animate with the images stored in (NSMutableArray *)frames, at random times. (UIImage *)filmstrip is just an image which contains all the frames which will be used on animation. The first iteration of animation works, but the problems appears on the second iteration, where I get -[UIImage _isResizable]: message sent to deallocated instance ... or -[UIImage _contentStretchInPixels]: message sent to deallocated instance ... or -[NSArrayI release]: message sent to deallocated instance .... This happens at
gun.animationImages = frames;
but I don't understand why. I'm not requesting a fix for my issue, but just to help me understand what's happening here. Thanks.
ARC is a mechanism that removes the need to manually retain/release objects. Here's a nice site that explains how this works: http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/
Try changing "leftGun" for "gun". I think that's probably the one that gets deallocated at some point, if you're using it through an ivar. Otherwise, leftGun simply isn't in the scope.
Here's what it should look like:
In your .h file:
#property (nonatomic, strong) IBOutlet UIImageView *leftGun;
In your .m file:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(arc4random() % 300)/100 * NSEC_PER_SEC), dispatch_get_current_queue(),^{
[self animateGun:gun withFilmStrip:[self getFilmStripForAction:gunShoot andTeam:nil withWeapon:nil]];
});
Also, not quite sure where "gunShoot" is coming from. Is that supposed to be an enum?
EDIT
Added an example of how the leftGun property should be defined. The reason behind using a property over an ivar is for memory management purposes. If you want to release or destroy an object that is a property, simply set it to nil and the property will take care of releasing the object if it has to.
You may prevent the deallocation of the frames array if you mark it as __block.
__block NSMutableArray *frames = [NSMutableArray array];
see “The __block Storage Type.”

After app comes from background "Message sent to deallocated instance"

I'm getting an odd error. We are using iOS 5 with ARC. When NSZombiesEnabled is set to true and the app is plugged into the debugger we get this error (it happens normally too but not as consistently)
2012-07-04 11:25:17.161 Trivial[624:707] -[vcCurrentGames gamesLoaded:] [Line 284] Found 62 games that are my turn.
2012-07-04 11:25:17.162 Trivial[624:707] -[vcCurrentGames gamesLoaded:] [Line 285] Found 26 games that are their turn.
2012-07-04 11:25:17.169 Trivial[624:707] -[vcCurrentGames tableView:heightForHeaderInSection:] [Line 409] Height 1: 29
2012-07-04 11:25:17.171 Trivial[624:707] *** -[vcDashboard retain]: message sent to deallocated instance 0xf62c3c0
We are not retaining the dashboard anywhere (ARC doesn't allow retain). This only happens after the app is loaded from the background. vcCurrentGames is actually a UITableView on the dashboard. Which makes it even more odd to me, because if the dashboard is dealloced then why is it's UITableView loading?
I've read a little bit about this. The dashboard is defined in the app delegate as a property:
#property (nonatomic, strong) vcDashboard *vDashboard;
I've attempted making this weak so that it will zero out, but that doesn't work either. Can someone tell me why it's being dealloced or why it's trying to retain vcDashboard after it's been dealloced?
In app delegate I declare it like this:
UIViewController *viewController = [[vcDashboard alloc] initWithNibName:#"vcDashboard" bundle:nil];
self.vDashboard = (vcDashboard *)viewController;
Maybe something goes wrong during initialization. You assign the vcDashboard to a UIViewController and then cast that controller to the appropriate class. While theoretically this should be fine, I have never seen this pattern before. The standard way is:
self.vDashboard = (vcDashboard*) [[vcDashboard alloc] init];
assuming that the nib name is "vcDashboard" (as seems to be the case) and that the class in the nib is also "vcDashboard".
(BTW, the convention is to capitalize class names.)
Also, after the app goes into the background, maybe vcDashboard gets deallocated. In any case, it is not guaranteed that it is still there when the app comes back from background. Did you consider lazy instantiation?
// in app delegate
-(vcDashboard*)vDashboard {
if (_vcDashboard) {
return _vcDashboard;
}
vcDasboard vc = [[vcDashboard alloc] init];
// more initialization code
_vcDashboard = vc;
return vc;
}

Crash in OS X Core Data Utility Tutorial

I'm trying to follow Apple's Core Data utility Tutorial. It was all going nicely, until...
The tutorial uses a custom sub-class of NSManagedObject, called 'Run'. Run.h looks like this:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Run : NSManagedObject {
NSInteger processID;
}
#property (retain) NSDate *date;
#property (retain) NSDate *primitiveDate;
#property NSInteger processID;
#end
Now, in Run.m we have an accessor method for the processID variable:
- (void)setProcessID:(int)newProcessID {
[self willChangeValueForKey:#"processID"];
processID = newProcessID;
[self didChangeValueForKey:#"processID"];
}
In main.m, we use functions to set up a managed object model and context, instantiate an entity called run, and add it to the context. We then get the current NSprocessInfo, in preparation for setting the processID of the run object.
NSManagedObjectContext *moc = managedObjectContext();
NSEntityDescription *runEntity = [[mom entitiesByName] objectForKey:#"Run"];
Run *run = [[Run alloc] initWithEntity:runEntity insertIntoManagedObjectContext:moc];
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
Next, we try to call the accessor method defined in Run.m to set the value of processID:
[run setProcessID:[processInfo processIdentifier]];
And that's where it's crashing. The object run seems to exist (I can see it in the debugger), so I don't think I'm messaging nil; on the other hand, it doesn't look like the setProcessID: message is actually being received. I'm obviously still learning this stuff (that's what tutorials are for, right?), and I'm probably doing something really stupid. However, any help or suggestions would be gratefully received!
===MORE INFORMATION===
Following up on Jeremy's suggestions:
The processID attribute in the model is set up like this:
NSAttributeDescription *idAttribute = [[NSAttributeDescription alloc]init];
[idAttribute setName:#"processID"];
[idAttribute setAttributeType:NSInteger32AttributeType];
[idAttribute setOptional:NO];
[idAttribute setDefaultValue:[NSNumber numberWithInteger:-1]];
which seems a little odd; we are defining it as a scalar type, and then giving it an NSNumber object as its default value. In the associated class, Run, processID is defined as an NSInteger. Still, this should be OK - it's all copied directly from the tutorial.
It seems to me that the problem is probably in there somewhere. By the way, the getter method for processID is defined like this:
- (int)processID {
[self willAccessValueForKey:#"processID"];
NSInteger pid = processID;
[self didAccessValueForKey:#"processID"];
return pid;
}
and this method works fine; it accesses and unpacks the default int value of processID (-1).
Thanks for the help so far!
If you are getting EXC_BAD_ACCESS on
[run setProcessID:[processInfo processIdentifier]];
it's almost certainly due to one of the pointers no longer pointing to a real object. Either run has been dealloc'd or processInfo has been dealloc'd. This assumes that the line is not the next line of code after
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
If it is, then both objects should be valid, so you are probably looking at something wrong with
[self willChangeValueForKey:#"processID"];
or
[self didChangeValueForKey:#"processID"];
if you have any objects observing that key, it's possible they have gone stale somehow.
This did not solve my problem with willAccessValueForKey but it solved a mystery. I subclassed an entity but I forgot to set the custom class in the model. The custom class worked until I wrote a method to send back a string which was a concatenation of some of the properties. The method was nearly identical to another method that was in another custom NSManagedObject class. I could not figure out why the one class worked and the other didn't. Once I set the custom class in the model they both worked.