Bad access when passing a UIImage as a parameter -iOS -Cocos2D - ios

Title is I believe self descriptive so I will start explaining my code..
This is the code that generates an image..
- (UIImage *) convertDataToImage {
NSData* imageData = [[NSUserDefaults standardUserDefaults] objectForKey:#"screenshot"];
return [UIImage imageWithData:imageData];
}
if I call this method like this [(AppDelegate*)[[UIApplication sharedApplication] delegate] sendEmailWithImage:[self convertDataToImage]]; then everything is fine, beside its speed.. coz I have heaps of methods that need the same image.. so I wanna call convertImageToImage method once only..
so I tried..
.h
UIImage *image;
.m init
image = [self convertDataToImage];
and lastly
[(AppDelegate*)[[UIApplication sharedApplication] delegate] sendEmailWithImage:image];
seems to be like everything should work as it was.. but I am getting a bad access error..
How can I fix this?
Thanks in advance..
Update: in my init I tried using the following code
CCTexture2D *texture = [[[CCTexture2D alloc] initWithImage:image] autorelease];
CCSprite *sp = [CCSprite spriteWithTexture:texture];
sp.position = ccp(100, 100);
[self addChild:sp];
and it is working.. but outside init still not working..
I am autoreleasing texture, does it also release my image?

Since you don't use ARC in your project, you should retain the UIImage instance returned by [self convertDataToImage]:
image = [[self convertDataToImage] retain];

Related

Objective C setCenter on Image

I am fairly new to iOS development. I have the following code
#property (nonatomic, strong) NSMutableArray *myImages;
-(void) viewDidLoad
{
[super viewDidLoad];
self.myImages = [[NSMutableArray alloc] init];
UIImageView *image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image1"]];
[self.myImages addObject:image];
[image setCenter:CGPointMake(10, 10)];
[self.view addSubview:image];
}
-(void) viewDidAppear:(BOOL)animated
{
for (;;) {
for (int i=0; i<[self.myImages count]; i++) {
[self.myImages[i] setCenter:CGPointMake(300, 300)];
}
}
}
When I do this, the image shows up at point 10,10. However it doesn't get changed to 300,300. Can any one suggest what I am doing wrong.
If I do something like
[self.myImages[0] setCenter:CGPointMake(300, 300)];
before the infinite loop, that works fine. but in the infinite loop no luck.
This code is just a snippet and a lot of the code is missing, but you should be able to understand what I am getting at with this.
Thanks
You forgot to add the imageView to the array. Add
[self.myImages addObject:image];
... in viewDidLoad.
As a side note: It's not common to abbreviate identifiers in Objective-C. Use imageView instead of image. Code becomes much easier to read.
You probably don't want the infinite loop in viewDidAppear, too.
first i'm kinda new to ios too so i am not sure.
i think there are 2 options:
i am not sure if view did appear is the right method to change the image i would suggest view will appear. and if you really want the one you used maybe try dispatch_async in order to change the image on the main ui thread.
Maybe try for in loop? that why you will set itrator as UIimage and it will recognize the image. this is less likly to solve it but maybe...

How to check NSCache instance value set or not

I have an instance of NSCache, like: NSCache *imageCache; It is basically used for holding some remote "image" values with different "#keys". I alloc & init NSCache in a Global class at the beginning and set a if else condition, like this:
if (self.imageCache == nil)
{
self.imageCache = [[NSCache alloc] init];
}
else
{
NSLog(#"cache set");
}
I #import that "Global Class" in all of ViewControllers, so that I don't have to parse images every time. But the problem is when I go to other ViewControllers It seems like, NSCache alloc & init every time. Because It takes same time to load the images as 1stVC. I think the if else condition didn't working perfectly or it's not the appropriate way to check either NSCache set or not.
Can anyone tell me whats wrong with it? One thing more, The imageCache is used from a global variable.
Thanks in advance.
Have a good day.
ADDITION:
This is the method where I load the UIButtons in UIScrollView as subView. This is a UIViewClass which I add in my "EveryViewController" as a subView Just take have a look on the if (cachedImage) line. It works fine. But when I want to check either the NSCache (iADImageCache) set or not, it shows me it's not set. But which should be set. In this situation how can I check all several iADImageCache with their different #"Key" name?
Thanks again.
-(void) loadUIButton
{
[self loadScrollView];
for (int i = 0; i < [iADDisplayArray count]; i++)
{
adButtonOutLet = [[UIButton alloc] initWithFrame:CGRectMake(i*320, 0, ButtonWidth, ButtonHight)];
currentAd = [iADDisplayArray objectAtIndex:i];
NSString *path = currentAd.bannerIconURL;
NSURL *url = [NSURL URLWithString:path];
NSMutableURLRequest *requestWithBodyParams = [NSMutableURLRequest requestWithURL:url];
NSData *imageData = [NSURLConnection sendSynchronousRequest:requestWithBodyParams returningResponse:nil error:nil];
UIImage *originalImage = [UIImage imageWithData:imageData];
UIImage *cachedImage = [self.iADImageCache objectForKey:currentAd.bannerIconURL];
if (cachedImage)
{
[adButtonOutLet setImage:cachedImage forState:UIControlStateNormal];
//NSLog(#"OnecachedImage %#", cachedImage);
}
else
{
[self.iADImageCache setObject:originalImage forKey:currentAd.bannerIconURL];
[adButtonOutLet setImage:originalImage forState:UIControlStateNormal];
NSLog(#"OneimageCache %#", self.iADImageCache);
}
adButtonOutLet.userInteractionEnabled= YES;
[adButtonOutLet setTag:i];
[adButtonOutLet addTarget:self action:#selector(goToURL:) forControlEvents:UIControlEventTouchUpInside];
[self.iADScrollView addSubview:adButtonOutLet];
}
}
You could make a singleton class, instance at application did finish launching and use it wherever you are, through all the view controllers. In the singleton class put a property
#property(nonatomic,strong) NSCache* imageCache;
then instance it just once in the singleton class init method. In this way you don't have to care about it, and you can just add images to that cache. Of course you have to check if the image is cached or not based on the existance of a key inside that cache.
NSCache* globalCache = [SingletonClass sharedInstanceMethod].imageCache;
UIImage *imageX = [globalCache objectForKey: #"keyX"];
if (!imageX) {
// download your image
imageX = <Download method>;
[globalCache setObject: imageX forKey: #"keyX"];
}
// Do your stuff (like showing the image)
....
...
..
.
Hope it helps
Unless actually needed across the whole program, I would suggest another approach that would restrict the caching to the class that actually needs to use it by adding the class method:
+ (NSCache *)staticCacheForClass
{
static NSCache *staticCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
staticCache = [NSCache new];
staticCache.name = #"myCache";
});
return staticCache;
}
Using this - you restrict the changes to the cache to be internal to the class that uses it and avoid creating another singleton. The cache is also preserved across creating and destroying instances of this class.
That said, a singleton could be a viable solution if you need to access the cache from different classes, since a cache singleton is not really a global state.
Cheers.

Objective-C Passing NSURL to another viewcontroller

I have two view controllers: FirstViewController and SecondViewController. The project is to record a video and attach it to an email. I can record it and have it able to email, I just need to attach it. I am able to get a URL for the video in FirstViewController, but passing it isn't working.
FirstViewController -
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:#"public.movie"])
{
// Saving the video to phone / // Get the new unique filename
NSString *sourcePath = [[info objectForKey:#"UIImagePickerControllerMediaURL"]relativePath];
UISaveVideoAtPathToSavedPhotosAlbum(sourcePath, self, /*#selector(video:didFinishSavingWithError:contextInfo:)*/nil, nil);
self.movieURL= [[NSURL alloc] initFileURLWithPath: sourcePath];
NSLog(#"movieURL");
NSLog(self.movieURL.absoluteString);
SecondViewController *sendMovie = [[SecondViewController alloc] initWithNibName:#"ViewController" bundle:nil];
sendMovie.movieU = [[NSURL alloc] initFileURLWithPath: sourcePath];
[[self navigationController] pushViewController:sendMovie animated:YES];
NSLog(#"movieU");
NSLog(sendMovie.movieU.absoluteString);
}
[picker dismissViewControllerAnimated:YES completion:NULL];
}
SecondViewController to attach to email -
NSLog(#"1st print");
NSLog(self.movieU);
[controller addAttachmentData:[NSData dataWithContentsOfURL:self.movieU] mimeType:#"video/MOV" fileName:#"defectVideo.MOV"];
NSLog(#"2nd print");
NSLog(self.movieU.absoluteURL);
[self presentViewController:controller animated:YES completion:nil];
SecondViewController.h property of NSURL
#property(nonatomic) NSURL *movieU;
The movieURL is a property in the FirstViewController, but I don't believe is needed.
I can see the URL in the FirstVC, but it appears nil in the SecondVC.
Solutions -
1) define NSURL *movieU as a extern instead of property.
2) else define it as a property of AppDelegate and access it.
3) or else synthesize property movieU and do allocate like -
in your FirstViewController -
SecondViewController *sendMovie = [[SecondViewController alloc] initWithNibName:#"ViewController" bundle:nil];
NSURL *url = [[NSURL alloc] initFileURLWithPath: sourcePath];
sendMovie.movieU = url;
[[self navigationController] pushViewController:sendMovie animated:YES];
And NSLog it in your SecondViewController.
Hope this helps!
There is nothing wrong with the way you are going about passing information. Try this:
// In you code where you create the second view controller.
SecondViewController *sendMovie = [[SecondViewController alloc] initWithNibName:#"ViewController" bundle:nil];
// SecondViewController viewDidLoad method being called (movieU is still nil)
sendMovie.movieU = [[NSURL alloc] initFileURLWithPath: sourcePath];
// Do a nil test here
if (!sendMovie.movieU) {
NSLog(#"%#",#"It's nil here!"); // If this is the case something is wrong with "sourcePath"
} else {
NSLog(#"%#",#"It's not nil here!");
}
[[self navigationController] pushViewController:sendMovie animated:YES];
// SecondViewController viewWillAppear method being called (movieU is NOT nil)
// SecondViewController viewDidAppear method being called (movieU is NOT nil)
This test will tell you a lot. If its nil here then the problem is not with setting the second view controller's property but with actually creating the NSURL object. It could be a lot of things like you are overwriting it somewhere in the second view controller (e.g. viewWillAppear method). Just follow the trail using break points, logging, etc. to follow its life time.
Edit - Debugging Procedure
Set break points where you create Second VC, set its property, push it, and finally view it (clicking on the tab). Set break points throughout the lifecycle of the second VC (viewDidLoad, viewWillAppear, viewDidAppear, etc). This will allow you to step through the entire lifecycle of your VC. If you haven't utilized those methods go ahead and define them anyway including a call to super. As you step through these break points keep checking the value of movieU. You will see it is nil at first, then you set it (we know this from your comments), its not nil, then it becomes nil, again, etc. If you are patient and systematic you will find it and learn a whole lot about the lifecycle of VCs. If you don't systematically step through you may miss something. For instance maybe your code is creating a whole new Second VC when you click on the tab to actually look at it. Stepping through your code this way will show that.
Edit
Make sure you are testing in the viewWillAppear/viewDidAppear methods or later so that the property has actually been set. See comments in my code.
Edit
A property of retainable object pointer type which is synthesized without a source of ownership has the ownership of its associated instance variable, if it already exists; otherwise, [beginning Apple 3.1, LLVM 3.1] its ownership is implicitly strong. Prior to this revision, it was ill-formed to synthesize such a property.
From: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#property-declarations
According to this (unless you are specifying weak on the ivar - unlikely) it should be strong by default.
Good luck.
use this
self.movieURL = [NSURL fileURLWithPath:sourcePath];
instead of
self.movieURL= [[NSURL alloc] initFileURLWithPath: sourcePath];
hope this work
I got it fixed! I decided to use a singleton which uses a shared instance through another class. I created a whole new class called Singleton and only contained the sharedinstance method:
+ (id)sharedInstance {
static id sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
Singleton.h
#property NSURL *movieU;
+ (id)sharedInstance;
Make sure to import the singleton class to both First and Second VC.
SecondVC.m
Singleton* single = [Singleton sharedInstance];
[controller addAttachmentData:[NSData dataWithContentsOfURL: single.movieU] mimeType:#"video/MOV" fileName:#"defectVideo.MOV"];
FirstVC.m
Singleton* single = [Singleton sharedInstance];
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:#"public.movie"]){
// Saving the video / // Get the new unique filename
NSString *sourcePath = [[info objectForKey:#"UIImagePickerControllerMediaURL"]relativePath];
UISaveVideoAtPathToSavedPhotosAlbum(sourcePath, self, /*#selector(video:didFinishSavingWithError:contextInfo:)*/nil, nil);
self.movieURL = [[NSURL alloc] initFileURLWithPath: sourcePath];
NSLog(#"movieURL");
single.movieU = self.movieURL;
By doing this, it successfully would pass the url containing the information for the recorded video to be attached to the email.

NSArray received memory warning

I have an array of images that changes to a random image with an IBAction attached to a button when pressed. When run on a simulator it runs fine but on a device it seems to crash after memory warnings. It also lags when the button is pressed. I want it to run smoothly, and not crash. I believe this has something to do with my array not releasing each image. Here is my array inside my buttons code.
-(IBAction)buttonPressed:(id)sender;
{
int ptr = arc4random() % 132;
NSArray* images = [[NSArray alloc] initWithObjects:#"17",#"29",#"55",#"400",#"Alcohol",#"Arianny",#"Anderson",#"Approach",#"Arab",#"Asian",#"Attitude",#"Attraction",#"Beckinsale",#"Blueberry",#"Brain",#"Break",#"Breakups",#"Burns",#"Buttocks",#"Charity",#"Check",#"Chicago",#"Chocolate",#"Coco",#"Coffee",#"College",#"Commit",#"Common",#"Confident",#"Cook",#"Count",#"Country",#"Couples",#"Courtship",#"Criminal",#"Cruz",#"Date",#"Date14",#"Deed",#"Degree",#"Dropped",#"Dushku",#"Dworaczyk",#"Eating",#"Emotion",#"Exercise",#"Fwb",#"Fantasies",#"Fitness",#"Flings",#"Flirt",#"Foot",#"Forget",#"Friendship",#"Frowning",#"Hum",#"Impression",#"Hair",#"Happiness",#"Hazel",#"Headache",#"Instant",#"Interest",#"Internet",#"Jacobs",#"January",#"Jimena",#"Jolie",#"Kalia",#"Kardashian",#"Kiss",#"Kissing",#"Krupa",#"Larissa",#"Latino",#"Laughter",#"Lip",#"London",#"Love",#"Love2",#"Love3",#"Love4",#"Math",#"Maximus",#"Melany",#"Memory",#"Men",#"Milian",#"Miller",#"Millions",#"Mind",#"Monica",#"Muscle",#"Partner",#"Naps",#"Negativity",#"Novels",#"Oral",#"Ossa",#"Pain",#"Positions",#"Productive",#"Proximity",#"Read",#"Reputation",#"Second",#"Sensitive",#"Serious",#"Shaking",#"Sleep2",#"Smile",#"Smoke",#"Smoke2",#"Smokers",#"Sneeze",#"Socks",#"Sold",#"Spot",#"Stimuli",#"Stone",#"Survey",#"Swell",#"Tattoo",#"Teacher",#"Teeth",#"Vickers",#"Violence",#"Wallet",#"Weight",#"Windmills.png",#"White",#"Women",#"Yawn",nil];
[imageView setImage:[UIImage imageNamed:[images objectAtIndex:ptr]]];
ptr++;
NSLog(#"button pressed");
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.images=nil;
}
- (void)dealloc {
[images release];
[adView release];
[super dealloc];
}
As I see your code is not running with ARC so when you create images array it is not deleted from memory until you call release. write release after you don't need your array anymore:
int ptr = arc4random() % 132;
NSArray* images = [[NSArray alloc] initWithObjects:#"17",#"29"];
[imageView setImage:[UIImage imageNamed:[images objectAtIndex:ptr]]];
ptr++;
[images release];
First off, use ARC, if you can.
You have 2 things leaking memory: the image and the array of image names. Since the image names are constant, you only need to create this array once.
Create ivars for the image and for the image name array:
UIImage *_image;
NSArray *_imageNames; // init this in your viewDidLoad:
Then, in your button press handler:
-(IBAction)buttonPressed:(id)sender;
{
int ptr = arc4random() % 132;
[_image release];
_image = UIImage imageNamed:_images[ptr]];
[imageView setImage:_image];
ptr++;
NSLog(#"button pressed");
}
Finally, release _imageNames:
- (void)dealloc
{
[_imageNames release];
// release everything else.
}
Again, you should really consider switching to ARC. You'll be glad you did.
You actually have two problems here, both surrounding this line:
NSArray* images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
The first is that the NSArray* at the beginning of the line declares a new local variable, images. This is separate from the property self.images that you try to erase in -viewDidUnload and release in -dealloc. Removing the NSArray* from the line will fix this issue, storing the array into the self.images property as you seem to intend.
That gives you a line like this:
images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
The second problem is that you re-create the images array each time you go through this method. That means that, even if you fixed the first problem, you would still be throwing away the old array without releasing it each time you passed through the method, so you'd still be leaking these arrays. There are a bunch of ways you could fix this, but the easiest one is probably to simply test if you already have an array and only create it if you haven't:
if(!images) {
images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
}
(Since all instances of this class have an identical list of image names, you could instead store the array in a static variable so it'd be shared between them—perhaps initialized by calling dispatch_once—but this isn't likely to make a difference unless you have many instances of this view controller on screen at the same time.)

updating UIImageView creates memory leak

I have researched hundreds of posts, but still cannot figure out where my problem is. I have an array of image names and I'm trying to randomly select an image and update the imageview on an event. When run on my device it crashes after 12-15 images.
- (void)viewDidLoad
{
[super viewDidLoad];
self.predictionArray = [[NSArray alloc] initWithObjects:#"IMG_0006.JPG",
#"IMG_0007.JPG",
#"IMG_0008.JPG",
#"IMG_0034.jpg",
#"IMG_0036.jpg",
#"IMG_0043.jpg",
#"IMG_0062.JPG",
#"IMG_0069.JPG",
#"IMG_0076.jpg",
#"IMG_0093.jpg",
#"IMG_0096.jpg",
#"IMG_0168.jpg",
#"IMG_0240.jpg",
#"IMG_0251.jpg",
#"IMG_0262.jpg",
#"IMG_0264.jpg",
#"IMG_0310.jpg",
#"IMG_0351.jpg",
#"IMG_0355.jpg",
#"IMG_0391.jpg",
#"IMG_0404.jpg",
#"IMG_0417.jpg",
#"IMG_0428.jpg",
#"IMG_0461.jpg",
#"IMG_0471.jpg",
#"IMG_0485.jpg",
#"IMG_0492.jpg",
#"IMG_0550.jpg",
#"IMG_0568.jpg",
#"IMG_0822.jpg", nil];
[self makePrediction];
}
- (void) makePrediction {
NSUInteger index = arc4random_uniform(self.predictionArray.count);
[self.pageImage setImage:[UIImage imageNamed:[self.predictionArray objectAtIndex:index ]]];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self makePrediction];
}
You are using the imageNamed: method to set the image of the imageview.But problem is imageNamed: method who has its own caching mechanism that you dont have any control over it.So when & where the allocated memory is released we don't know.So instead of using imageNamed: method use the following method to set the image.
NSString* imgpath= [ [ NSBundle mainBundle] pathForResource:#"sample" ofType:#"png"];
imgviw.image = [ UIImage imageWithContentsOfFile: imgPath];
The main advantage of using the imageWithContentsOfFile: method is this method does NOT cache the image, and therefore does not cause any memory issues with retaining large images. Also before applying image to imageview you can set the imgviw.image = nil & then set the image to avoid the memory leaks problems

Resources