I have been testing an IOS app on multiple platforms and have observed some weird behaviours while testing on the iPhone 4S. The app crashes after performing a task that works on both the iPhone 5 and iPhone 3S. When the crash happens there is nothing displayed on the debugger, absolutely no crash log or memory warnings appear in Xcode. I isolated the block of code, and the specific line responsible for the crash. I still don't understand why it is happening or how to find a solution. The code is part of a larger method that downloads a number of images and displays them in UIImageViews. Here is the code:
//fill image tiles with images
for (int i = 0; i < [objects count]; ++i)
{
PFObject *imageObject = [objects objectAtIndex:i];
PFFile *imageFile = (PFFile *)[imageObject objectForKey:#"image"];
UIImageView *imageHolder = (UIImageView *)[self.view viewWithTag:(100 + i)];
imageHolder.image = [UIImage imageWithData:imageFile.getData];
}
The code loops through an object that has been loaded with image files from a server. The UIImageViews that each image is meant to be assigned to are tagged from 100 - 104, so they can be accessed with the loop index variable. The line UIImageView *imageHolder = (UIImageView *)[self.view viewWithTag:(100 + i)] extracts the UIImageViews from the main view. In the next line the imageHolder view is assigned an image, this is the line that causes the crash, and when commented out the view loads without fail. I haven't been able to determine why this is happening or if it is the imageFile or imageHolder view that is not being set up properly. Perhaps someone here can shed some light on the problem.
You should have some protection around imageFile.getData as it could return nil. You should preferable use the getData: method which returns an error that you can use if the data can't be accessed.
NSError *error = nil;
NSData *imageData = [imageFile getData:&error];
if (imageData != nil) {
imageHolder.image = [UIImage imageWithData:imageData];
} else {
NSLog(#"OMG!! %#", error);
}
Related
Im attempting to save some images to a camera roll, all the images are in a array.
if (buttonIndex == 1) {
int done = 0;
NSMutableArray *copyOfImages = [[NSMutableArray alloc]initWithArray:saveImagesToArray];
while (!done == 1){
if (copyOfImages.count == 0) {
done = 1;
}
if (copyOfImages.count > 0){
[copyOfImages removeLastObject];
}
UIImage *image = [[UIImage alloc]init];
image = [copyOfImages lastObject];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
because i dont know how many images there can be i use a while loop
Im testing this with 8 images, the array shows a count of 8 so thats good.
When i try saving the images this comes up in the console.
-[NSKeyedUnarchiver initForReadingWithData:]: data is NULL
Out of the 8 images im trying, only 5 show up in the camera roll.
Any help would be great.
Thanks :)
Why are you calling [copyOfImages removeLastObject]?
Every time you go through that look are you destroying the last object, which is strange because you haven't added it to the roll yet. Take out that line and see if you look works.
Also, rather than using a for loop, use the following pattern:
for (id object in array) {
// do something with object
}
This will enumerate though the objects in the array, stopping when it reaches the end. Just be sure not to modify the array while you are doing this.
I had this same issue and I resolved it by ensuring that the images are saved sequentially. I think there may be some kind of race condition going on.
Basically, I did:
UIImageWriteToSavedPhotosAlbum([self.images objectAtIndex:self.currentIndex], self,
#selector(image:didFinishSavingWithError:contextInfo:), nil);
Then in image:didFinishSavingWithError:contextInfo:, I increment currentIndex and try the next image, if there are any left.
This has worked for me in every case so far.
I had the same exact problem. No matter how much pictures i added to the share activityController, maximum of 5 were saved.
i have found that the problem was to send the real UIImage and not URL:
NSMutableArray *activityItems = [[NSMutableArray alloc] init];
for(UIImage *image in imagesArray) {
[activityItems addObject:image];
}
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
To expand on Bill's answer, UIImageWriteToSavedPhotosAlbum seems to do its writing on some unknown thread asynchronously - but also there is some hidden limit to the number of images it can write at once. You can even tease out write busy-type errors if you dig in deep.
Tons more info here:
Objective-C: UIImageWriteToSavedPhotosAlbum() + asynchronous = problems
I also agree with Bill that serializing your writes is the only reasonable/reliable answer
I have seen.
I've read a lot of UIScrollView with UIImageView threads here or other googled pages. But I still cannot get the problem I'm confronting. I'm having a cold right now. Hope I can still make it clear, lol. Here is the problem:
I'm building one app which mainly uses UIScrollView to display a few images. Here the amount counts, not the size, which is averagely 100KB(I even converted PNG to jpg, not sure whether it helps or not). With no more than 10 images, my app crashes with memory warning. This is the first time I encounter memory issue, which surprised me as the compiled app is less than 10MB.
At the very beginning, I load all the images on launch, looping all names of image files and do
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
[scrollview addSubview:imageView];
[imageView release];
If I'm right, I think after launch, all the images are in memory, right? But the funny thing here is, the app could launch without any problem(at most a level 1 memory warning). After I scroll a few images, it crashed. I checked leaks for sure and also allocations. No leak and allocation almost had no change during scrolling.
So, is there something special done by imageNamed rather than cache?
And then, yes, I turned to lazy load.
For fear of checking page and loading images on demand might jerk the scrolling(which was proved true), I used a thread which runs a loop to check offset of the scroll view and load/unload images.
I subclassed UIImageView by remembering the image name. It also contains loadImage and unloadImage which will be executed on that thread.
- (void)loadImage {
/if ([self.subviews count] == 0) {
UIImageView iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.imageName]];
[self performSelectorOnMainThread:#selector(renderImage:) withObject:iv waitUntilDone:NO];
//[self addSubview:iv];
[iv release];
}*/
if (self.image == nil) {
//UIImage *img = [UIImage imageNamed:self.imageName];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[self.imageName stringByDeletingPathExtension] ofType:[self.imageName pathExtension]]];
// image must be set on main thread as UI rendering is main thread's responsibility
[self performSelectorOnMainThread:#selector(renderImage:) withObject:img waitUntilDone:NO];
[img release];
}
}
// render image on main thread
- (void)renderImage:(UIImage*)iv {
//[self addSubview:iv];
self.image = iv;
}
(void)unloadImage {
self.image = nil;
//[(UIView*)[self.subviews lastObject] removeFromSuperview];
}
You can see the commented code that I've played with.
In unloadImage, if I write [self.image release], then I get EXC_BAD_ACCESS, which is unexpected, as I think alloc and release are matched here.
The app still crashes with no leak. The initWithContentsOfFile version even crashed earlier than imageNamed version, and made the scrolling not that smooth.
I run the app on device. By checking allocations, I found imageNamed version used much less memory than initWithContentsOfFile version, though they both crash. Instruments also showed that the allocated images were 2,3 or 4, which indicated the lazy load did do his job.
I checked PhotoScroller of WWDC2010, but I don't think it solvs my problem. There is no zooming or huge picture involved.
Anybody helps! Thank you in advance.
The crash log says nothing. The app crashes mostly after memory warning level = 2. And if run on simulator, there will be no problem.
It doesn't matter which format do you use for your images. They're converted to bitmaps when you display them.
I'd suggest to use the technique similar to that one which is used by UITableView (hide the image and free the memory it uses when it disappears from the screen and instantiate the image only when you need to show it).
As an alternate way – if you need to show these images in a grid – you might take a look to a CATiledLayer.
Anyhow, loading all the images to the memory is not the best idea :)
You can load all the images to an array. And you can design a view having one image view and try the below code:
array name: examplearray and view name :exampleview
-(void)updateImagesInScrollView
{
int cnt = [examplearray count];
for(int j=0; j< cnt; ++j)
{
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"exampleview"
owner:self
options:nil];
UIView *myView = [nibContents objectAtIndex:0];
exampleview * rview= (exampleview *)myView;
/* you can get your iamge from the array and set the image*/
rview.imageview.image = yourimage;
/*adding the view to your scrollview*/
[self.ScrollView addSubview:rview];
}
/* you need to set the content size of the scroll view */
self.ScrollView.contentSize = CGSizeMake(X, self.mHorizontalScrollView.contentSize.height);
}
I have a UITableView which uses the following code to display an image in a Table View cell:
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = 5.0;
UIImage *image = [UIImage imageNamed:[[color types] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
if ( image ) {
cell.imageView.image = [image imageScaledToSize:CGSizeMake(50, 50)];
}
It works fine on the iPhone simulator, but when I try it on a real iPhone the iPhone doesn't show. Instead in the console in debugging mode, I get this error:
attempt to pop an unknown autorelease
pool (0x851e00)
Any help would be great, thanks.
Check that image != nil. If it == nil then problem in that [[color types] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] code.
Generated file name must be equal real file name including extension, case and must not contain whitespaces.
Im developing an app for an iPhone and I found that the following code is causing the memory allocation to increment.
-(UIImage *)createRecipeCardImage:(Process *)objectTBD atIndex:(int)indx
{
[objectTBD retain];
// bringing the image for the background
UIImage *rCard = [UIImage imageNamed:#"card_bg.png"];
CGRect frame = CGRectMake(00.0f, 80.0f, 330.0f, 330.0f);
// creating he UIImage view to contain the recipe's data
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = rCard;
[rCard release];
imageView.userInteractionEnabled = YES;
float titleLabelWidth = 150.0;
float leftGutter = 5.0;
float titleYPos = 25.0;
float space = 3.0;
float leftYPos = 0;
// locating Title label
float currentHeight = [self calculateHeightOfTextFromWidth:objectTBD.Title :titleFont :titleLabelWidth :UILineBreakModeWordWrap];
UILabel *cardTitle = [[UILabel alloc]initWithFrame:CGRectMake(leftGutter, titleYPos, titleLabelWidth, currentHeight)];
cardTitle.lineBreakMode = UILineBreakModeWordWrap;
cardTitle.numberOfLines = 0;
cardTitle.font = titleFont;
cardTitle.text = objectTBD.Title;
cardTitle.backgroundColor = [UIColor clearColor];
[imageView addSubview:cardTitle];
[cardTitle release];
leftYPos = titleYPos + currentHeight + space;
// locating brown line
UIView *brownLine = [[UIView alloc] initWithFrame:CGRectMake(5.0, leftYPos, 150.0, 2.0)];
brownLine.backgroundColor = [UIColor colorWithRed:0.647 green:0.341 blue:0.122 alpha:1.0];
[imageView addSubview:brownLine];
[brownLine release];
leftYPos = leftYPos + 2 + space + space + space;
// creating the imageView to place the image
UIImageView *processPhoto = [[UIImageView alloc] initWithFrame:CGRectMake(leftGutter, leftYPos, 150, 150)];
if((uniqueIndex == indx) && (uniqueImg.imageData != nil))
{
if([uniqueImg.rcpIden isEqualToString:objectTBD.iden])
{
objectTBD.imageData = [NSString stringWithFormat:#"%#", uniqueImg.imageData];
[recipesFound replaceObjectAtIndex:indx withObject:objectTBD];
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
[imageData release];
processPhoto.image = rcpImage;
[rcpImage release];
}
}
else if(objectTBD.imageData != nil)
{
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
processPhoto.image = rcpImage;
[rcpImage release];
[decodedBigImageDataPointers addObject:imageData];
}
else
{
UIImage * rcpImage = [UIImage imageNamed:#"default_recipe_img.png"];
processPhoto.image = rcpImage;
[rcpImage release];
}
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 1
[imageView addSubview:processPhoto];
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 2!!!!
//[processPhoto release]; // this line causes an error :(
// converting the UIImageView into a UIImage
UIGraphicsBeginImageContext(imageView.bounds.size);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[objectTBD release];
for(UIView *eachSubview in imageView.subviews)
{
[eachSubview removeFromSuperview];
NSLog(#"each subview retainCount %i despues", [eachSubview retainCount]);
// here I find that the processPhoto view has a retain count of 2 (all other views have their retain count in 1)
}
return viewImage;
}
When I checked at the instruments object allocation I found that the "GeneralBlock-9216" growing up.
Breaking down the row I found that every time I call this code, one instance of:
2 0x5083800 00:18.534 ImageIO initImageJPEG
is being allocated. Checking the call stack, the following line is highlighted:
UIImage * objImage = [UIImage imageWithData:imageData];
Any help to find what the error is?
As TechZen said, the imageWithXXX: methods cache the image inside of them while you run the program (though you release the instances after using). I recommend initWithXXX: and release API sets instead of imageWithXXX:.
Well, if you embed several debug log on your source code, check how many times is it called, and check the retain count of the instances.
As far as I can explain, that is all.
I hope you will solve the problem.
Does anyone have an answer for this? It's tearing me apart trying to figure out why this image information keeps lingering. I've tried every solution.
The situation:
Images get downloaded and stored to the device, then loaded with imageWithContentsOfFile (or even initWithContentsOfFile, which doesn't help either). When the view goes away, the images don't, but they don't show up as leaks, they're just this initImageJPEG Malloc 9.00 KB that never goes away and keeps ramping up.
UPDATE: I believe I've figured this out: Check to make sure everything is actually getting dealloc'd when you're releasing whatever the parents (and/or grandparents) and etc of the images are. If the parents don't get deallocated, they never let go of their children images, and whatever data was in those images sticks around. So check retain counts of parent objects and make sure that everything's going away all the way up whenever you release the view at the top.
A good way to check for this is to put NSLogs into custom classes' dealloc methods. If they never show up, that object isn't going away, even though the reference to it might, and it (and whatever its subviews and properties are) will never ever disappear. In the case of images, this means a pretty sizable allocation every time that object is generated and never deallocated. It might not show up in leaks, especially if the parent of the topmost object you're thinking you're releasing but actually aren't persists and doesn't itself ever deallocate.
I hope this helps. It'll be useful to take some time to read through your code with a fine-toothed comb to make sure you're allocating and releasing things properly. (Search for "alloc]", start at the top of the file, and work your way down to make sure you're releasing and that the release isn't inside of some if() or something.)
Also, running "Build and Analyze" might lock up your machine for a bit, but its results can be really helpful.
Good luck!
I think you're seeing UIImage cacheing images. There used there used to be a method something like initWithData:cache that let you turn the cacheing off. Now I think the system always caches the images automatically behind the scenes even after you've deallocted the specific instances.
I don't think its an error on your part. I think it's the system keeping data around in the OpenGl subsystem. Unless it causes a major leak, I don't think it is a problem.
I am developing an game in which i have to load images on finger swipe. For this i loaded all image 22 of size (788x525) at loading of the view itself. Then on finger swipe i just added image on view.
This works fine on iTouch but on iPhone game crashes after showing near about 12 images.
This is how i added images, graphicsArray is NSMUTableArray.
for ( int i=0; i<=totalNoofPages;i++){
NSString * graphicsFileName = #"Page";
graphicsFileName =
[graphicsFileName
stringByAppendingString:[NSString stringWithFormat:#"%d", i]];
UIImage *image =
[[UIImage alloc] initWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:graphicsFileName
ofType:#"jpg"]];
GraphicsImages =
[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 480,320 )];
GraphicsImages.image = image;
[image release];
[GraphicsImages setCenter:center];
GraphicsImages.transform = CGAffineTransformMakeRotation(M_PI/2);
[graphicsArray insertObject:GraphicsImages atIndex:i];
}
and on finger swipe i write,pageNo is number of page that will be displayed.
GraphicsImages = [graphicsArray objectAtIndex:pageNo];
[self addSubview:GraphicsImages];
Is there any way to release the previous loaded image from sub view as it might be kept on stack or is it due to large image size????
Please help me in this situation.
Thanks in advance.
Regards,
Vishal.
Are you crashing due to lack of memory?
You might try pre-loading the previous, current & next images only, and release them as soon as possible. Loading 22 images in memory takes up quite a lot of space.
Can you provide the error that you're getting when the app crashes? Knowing what exception is being triggered will help a lot in figuring out exactly what's going on.
I can tell you that you aren't releasing the GraphicsImages variable in your for loop, so that's causing memory leaks. I imagine that that's not enough to cause a crash, however. In any case, do this:
[ graphicsArray insertObject:GraphicsImages ];
[ GraphicsImages release ];
Note that you don't need to specify the insertion index in your case because the items will be added sequentially. Also, when you insert an object into an array, it is retained, so you can safely release it afterwards.