I am calling a class method from an outside class in order to obtain an object reference, in this case a reference to a UIImage. I can successfully call it and get it back from within myOtherClass itself, but when I call the method from myClass, it always returns nil.
#implementation myClass
...
- (UIImage *) gethThumb: (UIImage *) originalImage {
// always comes back nil:
UIImage* thumb = [MyOtherClass makeThumb: originalImage];
return thumb;
}
...
#end
#implementation MyOtherClass
...
+ (UIImage*) makeThumb: (UIImage *) fullImage {
CGSize imageSize = [fullImage size];
int shortestEdge = MIN(imageSize.width, imageSize.height);
CGRect rect = CGRectMake((imageSize.width - shortestEdge)/2, (imageSize.height - shortestEdge)/2, shortestEdge, shortestEdge);
CGImageRef imageRef = CGImageCreateWithImageInRect([fullImage CGImage], rect);
UIImage *thumb = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGSize thumbsize = CGSizeMake(180, 180);
UIGraphicsBeginImageContext(thumbsize);
[thumb drawInRect:CGRectMake(0, 0, thumbsize.width, thumbsize.height)];
UIImage *scaledThumb = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//scaledThumb is not nil here:
return scaledThumb;
}
...
#end
I stepped through and watched the value, I could see it was pointing to a valid memory address while still inside the makeThumb method, but once it had returned the pointer to the external class, the memory address was suddenly 0.
I tried writing it as an instance method and instantiating an object and then calling the method using
thumb = [self.myInstanceOfOtherClass makeThumb: originalImage];
and also changing the method to accept an object reference:
UIImage *thumbRef = nil;
thumb = [self.myInstanceOfOtherClass makeThumb: originalImage thumbRef:thumbRef];
-- in the myOtherClasss:
- (UIImage*) makeThumb: (UIImage *) fullImage thumbRef:(UIImage *) thumbRef {
...
thumbRef = scaledThumb;
...
}
... which I thought would prevent ARC from losing the reference to the object (if that's what's happening here).
I am relatively new to Objective C, and I'm hoping someone can explain to me why this is happening. Is the object being deallocated before it returns to the external class? Do I have to turn it into a property that is strong in the external class so the reference doesn't get discarded?
Forgot to mention: the reference is pointing to an image being created from a reference to a camera image in the UIImagePickerControllerDelegate method, didFinishPickingMediaWithInfo. The Camera UIView is dismissed before this thumb is created. I think maybe this is why I am losing the reference.
Thanks in advance for your help!!
You never allocated the memory for image. DO the following:
UIImage *myimage =[ [ UIImage alloc] init].
Related
I want to call a method B using a different method A. The problem is that the aksed parameters of method B is not present in Method A.
here's what I've tried ..
-(void) methodA {
// some code
CGSize *size = [CGSizeMake(self.frame.size.width, self.frame.size.height)];
[self methodB:size];
}
-(void) methodB:(CGSize) size {
//some code
}
There is certainly a better way...
Thanks
I don't think your code will be compiled.
CGSize is not an object.
Refactor to:
CGSize size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self methodB:size];
You should only wrap [] around methods. CGSizeMake(self.frame.size.width, self.frame.size.height) is a function, not a method, so get rid of the square braces. You can tell it's a function because functions look like functionName(argument1, argument2, ...) whereas methods look like [object methodName:argument1 methondNameContinued:argument2].
Further, CGSizeMake returns a CGSize structure, not a pointer to a CGSize structure, so ditch the * in *size.
Then you'll be left with this:
CGSize size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self methodB:size];
Which is correct.
Could someone help me understand the primitive accessors with this example : i don't understand what is automatically set and the order of those methods :
1.after a person is created, is willSave the first method called? (i guess so, because save: is called after we create a person with insertNewObjectForEntityForName )
2.in RootViewController (the second chunk of code), we then call the getter of eyeColor with : person.eyeColor :
a) in eyeColor, we call : [self eyeColorData] ,
b) but setPrimitiveEyeColorData is in willSave, which is accessible only if primitiveEyeColor exists,
c) but setPrimitiveEyeColor is in eyeColor and only called if [self eyeColorData] exists. So, i'm a bit confused with this code, could someone help me?
here's the code about eyeColor and eyeColorData :
#dynamic eyeColorData;
#dynamic eyeColor;
#interface AWPerson (PrimitiveAccessors)
- (UIColor *)primitiveEyeColor;
- (void)setPrimitiveEyeColor:(UIColor *)value;
- (NSData *)primitiveEyeColorData;
- (void)setPrimitiveEyeColorData:(NSData *)value;
#end
+ (id)personInManagedObjectContext:(NSManagedObjectContext *)moc {
return [NSEntityDescription
insertNewObjectForEntityForName:#"Person"
inManagedObjectContext:moc];
}
+ (id)randomPersonInManagedObjectContext:(NSManagedObjectContext *)moc {
AWPerson *randomPerson = [self personInManagedObjectContext:moc];
//...
randomPerson.eyeColor = [self randomColor]; //setter eyeColor
return randomPerson;
}
+ (UIColor *)randomColor {
static NSArray *colorsArray = nil;
if( !colorsArray ) {
colorsArray = [[NSArray alloc] initWithObjects:
[UIColor lightGrayColor],
[UIColor blueColor],
[UIColor greenColor], nil];
}
int randomIndex = arc4random() % [colorsArray count];
return [colorsArray objectAtIndex:randomIndex];
}
- (void)willSave {
UIColor *color = [self primitiveEyeColor];
if( color ) {
[self setPrimitiveEyeColorData:
[NSKeyedArchiver archivedDataWithRootObject:color]];
} else {
[self setPrimitiveEyeColorData:nil];
}
[super willSave];
}
- (UIColor *)eyeColor {
[self willAccessValueForKey:#"eyeColor"];
UIColor *tmpValue = [self primitiveEyeColor];
[self didAccessValueForKey:#"eyeColor"];
if( tmpValue ) return tmpValue;
NSData *colorData = [self eyeColorData];
if( !colorData ) return nil;
tmpValue = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];
[self setPrimitiveEyeColor:tmpValue];
return tmpValue;
}
in RootViewController :
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
AWPerson *person = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[cell setBackgroundColor:person.eyeColor];
}
Thanks
EDIT - Added info on willSave
To answer your first question, willSave is called whenever the object is saved (using the save method). So the first method called will be one of the class methods (used to create the object) or init and then, since you said that the object is saved just after it is created, willSave gets called.
I think the key to understanding this is to realize that eyeColor, primitiveEyeColor, and their setters are all ultimately interacting with the same variable in memory (the iVar named eyeColor). The difference is whether or not the code in the setter/getter (in this case the - (UIColor *)eyeColor { function) is called.
There are just a few different ways to interact with it:
[self primitiveEyeColor]; - This reads the value of the iVar directly.
[self setPrimitiveEyeColor:tmpValue]; - This sets the value of the iVar directly.
[self eyeColor] - This calls the - (UIColor *)eyeColor method in your class (which should ultimately retrieve the iVar or a representation of it).
[self setEyeColor:value] - This calls the - (void)setEyeColor:(UIColor *)newColor method in your class. Note that in this case it doesn't exist so it simply calls the primitive method (and does the KVO magic).
In this particular code, they are using a "non-standard persistent attribute" because NSManagedObject does not support UIColor's. Read about it here.
EDIT 2
To answer your other questions:
a) The color in randomPerson.eyeColor = [self randomColor] is
accessible with [self primitiveEyeColor] (in willSave)?
Yes, once eyeColor is set (either via the setEyeColor method or the setPrimitiveEyeColor method), you can read it from primitiveEyeColor and it will return the same value.
Note that once it is set, eyeColor and primitiveEyeColor return the same value and can be called from anywhere in your class (not just willSave).
b) So if [self primitiveEyeColor] != nil : in eyeColor, the line :
if( tmpValue ) return tmpValue; should therefore always be true...
when can we unarchive eyeColorData if UIColor *tmpValue = [self
primitiveEyeColor] is always returned in -(UIColor *)eyeColor?
This method only looks at eyeColorData (which was stored during the last call to willSave) if eyeColor is nil. This is an optimization because we could skip all of this and just unarchive eyeColorData every time if we wanted to. In this case, once a value is unarchived or set to a new value, it always stores that value and returns it so that we don't have to call unarchive again.
Also, there is really what I believe to be an error here (although it could be by design). Let's say that we perform the following steps:
Set eyeColor to a random color (let's say blue).
save the object.
Set eyeColor to nil
Now, if you check the color using [self eyeColor] it will see that primitiveEyeColor is nil and unarchive eyeColorData again, therefore returning the blue color that was stored previously. You should probably be over-riding the set function so that it sets eyeColorData to nil when eyeColor is set to nil. That way, checking the value of eyeColor after setting it to nil will return nil as expected.
This has to be very basic, but I don't see the problem. The program crashes whenever the following code block is executed. Analyzer reports a possible memory leak:
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [anImage scaleToFitWithin:defaultEventImageFrame.size interpolationQuality:kCGInterpolationHigh];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
}
The message is -[UIImage release]: message sent to deallocated instance 0x1129d920*
Instance 0x1129d920 is scaledImage
I tried adding retains and releases, like this
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [[anImage scaleToFitWithin:defaultEventImageFrame.size interpolationQuality:kCGInterpolationHigh] retain];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
[scaledImage release];
}
and still get the error message.
So I tried replacing the assignment with a copy, like this
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [anImage copy];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
}
And the problem is gone.
Checking the scaleToFitWithin method, I see it returns an autoreleased object:
- (UIImage *) scaleToFitWithin:(CGSize) newSize
interpolationQuality:(CGInterpolationQuality)quality{
CGSize originalImageSize = self.size;
CGSize newImageSize;
if (originalImageSize.width <= originalImageSize.height) {
newImageSize.width = self.size.width * newSize.width / self.size.width;
newImageSize.height = self.size.height * newSize.width / self.size.width;
}
else {
newImageSize.width = self.size.width * newSize.height / self.size.height;
newImageSize.height = self.size.height * newSize.height / self.size.height;
}
return [[[self normalize] resizedImage:newImageSize interpolationQuality:kCGInterpolationHigh] autorelease];
}
So there is something about memory management that I'm not understanding. What is the problem likely to be?
The problem is most likely that the scaleToFitWithin:interpolationQuality: method is calling autorelease on an object which has already previously been autoreleased. This can occur if you initialise the UIImage using a temporary constructor, like +[UIImage imageWith...], earlier in the same method you call the scaling method from. The reason it works when you use [anImage copy] is because the behaviour of the copy constructor is such that the object returned to you has already had retain called on it (so it has a local retain count of 1 and zero autoreleases).
What happens at the end of the current run loop is: the autorelease pool which is currently in use is drained, and as a part of that two release messages will be sent to the UIImage. When the first one is sent, the application then runs off and calls dealloc on the image, because the retainCount has decreased to zero. When the second one is sent, the application throws an exception because a message is being sent to an object which was previously deallocated.
Try removing the autorelease message from the scaleToFitWithin:interpolationQuality: method. Even if your resizedImage:interpolationQuality: method IS returning a new object, you should only be calling autorelease in that method rather than the scaling method.
It seems that the method resizedImage:interpolationQuality: itself returns an autoreleased object and you are again autoreleasing it in the reutun statement. Just remove the autorelease from the return statement,
return [[self normalize] resizedImage:newImageSize
interpolationQuality:kCGInterpolationHigh];
Then you don't have to retain/release or copy the returned object in if (anImage) {...} block.
I am making a simple game that requires that I draw multiple UIImages in many different places. My problem here is that I need to add the UIImages to an NSMutableArray, and access them later. Using NSStrings to represent the images (such as for paths) will not work here, so I need to know how to change the images for every UIImage in the array.
My code is as follows (at least, for accessing the NSMutableArray). The NSMutableArray is declared in my .h file, and is initialized in a different method.
for (int a = 0; a <= [theArray count]; a ++) {
// I make a UIImage, which I will draw later
UIImage *theImage = [theArray objectAtIndex:a];
// then I do the drawing
}
This works fine. My problem is that I cannot figure out how to change a particular object in the NSMutableArray. How can I do this?
By the way, I am adding the UIImages to the NSMutableArray with the following code.
+ (void) createCarWithImage:(NSString*)theImageName {
UIImage *anImage= [UIImage imageNamed:theImageName];
[theArray addObject:anImage];
}
I think you want to use this method:
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
e.g.:
[theArray replaceObjectAtIndex:4 withObject:someOtherImage];
Just modify each UIImage in the NSMutableArray as is, you only have a pointer to the actual data. That is, regardless of what pointer you use to change the data, the data is changed for every pointer.
Additionally, you can also iterate over your UIImages with:
for (UIImage *img in theArray)
{
//send messages to img
}
Answer to Comment
You can modify the UIImage at index 4 by:
UIImage *image = [theArray objectAtIndex:4];
//send messages to image
How to release this variable with no EXC_BAB_ACCESS ?
//First line create memory leak
UIImage *ImageAvatar = [[UIImage alloc] initWithData:[myg.imageData copy]];
Moins1 = ImageAvatar;
//[ImageAvatar release]; if i release-> EXC_BAD_ACCESS
Moins1 is a menber of the interface is declared like this :
UIImage *Moins1;
...
#property (nonatomic, retain) UIImage *Moins1;
It looks like the problem isn't the UIImage, but rather the NSData. In Cocoa, any copy (or mutableCopy) method returns an object with a +1 retain count, meaning that you own it and are therefore responsible for releasing it.
In your code, you're calling -copy on myg.imageData, but never releasing it. That's a classic example of a memory leak. Here's what I would do to fix it, plus with changing your syntax a bit:
ivar:
UIImage *Moins1;
#property (nonatomic, retain) UIImage *Moins1;
implementation:
NSData * imageData = [myg.imageData copy];
UIImage * ImageAvatar = [[UIImage alloc] initWithData:imageData];
[imageData release];
[self setMoins1:ImageAvatar];
[ImageAvatar release];
You should not need to send -copy to the NSData object. UIImage does not keep a reference to the data around, it just reads it and produces an image. Sending -copy without -release is a memory leak.
However, that does not explain the EXC_BAD_ACCESS. Something else is going on, and not from the code you've posted.
There are two problems in your code. The copying of imageData as indicated by the other contributers, and the assignment to Moins1 field without retaining the object.
The assingment to Moins1 access the field directly, so you would need to do your own retaining. If you don't retain it and release it in the next line, then any subsequence access to the field results into a protection error.
You can use the property for assignment:
UIImage *ImageAvatar =
[[UIImage alloc] initWithData:[[myg.imageData copy] autorelease]];
self.Moins1 = ImageAvatar;
[ImageAvatar release];
Or you can also just do it in one line:
self.Moins1 = [UIImage imageWithData:[[myg.imageData copy] autorelease]];
-(void )dealloc
{
if(self.Moins1!=nil)
{
self.Moins1 = nil;
}
}
see when u give and object a retain property its count is already 1 and when u allocate it its count becomes 2 so when the dealloc is called it will check if its nil and if its not nil make it nil .In this way it will give the retain count of the variable to 0