iOS : When saving photos to the folder memory overflow - ios

I use instruments to detect my program, the following code detects memory leaks。
Looking forward to your help, thank you!
int count=(int)array.count;
for (int i=0; i<count; i++) {
set=(ALAsset *)[array objectAtIndex:i];
CGImageRef ref1= [set thumbnail];
CGImageRef ref=[[set defaultRepresentation] fullResolutionImage];
NSData * imagedata2=[[NSData alloc]init];
imagedata2=UIImageJPEGRepresentation([[UIImage alloc]initWithCGImage:ref],1);
// CGImageRelease(ref);
NSString * photoNmaeOgiginal=[NSString stringWithFormat:#"%#_origianl.jpg",uuid];
NSString *PhtotoOriginalPath=[AlbumFolderPath stringByAppendingPathComponent:photoNmaeOgiginal ];
BOOL resultOriginal= [NSData writeToFile:PhtotoOriginalPath atomically:YES];
}

There is one obvious issue with the code you posted, the line NSData * imagedata2=[[NSData alloc]init]; is totally not need since the next line just replaces the object.
Which would trigger memory leak
So replace:
NSData * imagedata2=[[NSData alloc]init];
imagedata2=UIImageJPEGRepresentation([[UIImage alloc]initWithCGImage:ref],1);
With
NSDate *imagedata2=UIImageJPEGRepresentation([[UIImage alloc]initWithCGImage:ref],1);

Related

NSDictionary constant looping

SO I have a program which calls the FlickR API, gets the URL's puts them into a dictionary and then assigns them into a table view, using an image view.
NSArray *photos = [self.flickr photosForUser:#"James Kinvig"];
int countAttempts = 0;
[[self.flickr photosForUser:#"James Kinvig"]count];
for (int i = 0; i < [[self.flickr photosForUser:#"James Kinvig"]count]; i++) {
for(NSDictionary *dictionary in photos) {
countAttempts++;
NSString *farmId = [dictionary objectForKey:#"farm"];
NSString *serverId = [dictionary objectForKey:#"server"];
NSString *photoId = [dictionary objectForKey:#"id"];
NSString *secret = [dictionary objectForKey:#"secret"];
self.url= [NSURL URLWithString:[NSString stringWithFormat:#"http://farm%#.staticflickr.com/%#/%#_%#.jpg", farmId, serverId, photoId, secret]];
//NSLog(#"self.url = %#", self.url);
NSLog(#"count = %d", countAttempts);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:self.url];
dispatch_sync(dispatch_get_main_queue(), ^{
UIImage *img = [UIImage imageWithData:imgData];
cell.imageView.image = img;
[cell setNeedsLayout];
});
});
}
}
return cell;
}
This is the method it calls, photosForUser:
- (NSMutableArray *) photosForUser: (NSString *) friendUserName
{
NSString *request = [NSString stringWithFormat: #"https://api.flickr.com/services/rest/?method=flickr.people.findByUsername&username=%#", friendUserName];
NSDictionary *result = [self fetch: request];
NSString *nsid = [result valueForKeyPath: #"user.nsid"];
request = [NSString stringWithFormat: #"https://api.flickr.com/services/rest/?method=flickr.photos.search&per_page=%ld&has_geo=1&user_id=%#&extras=original_format,tags,description,geo,date_upload,owner_name,place_url", (long) self.maximumResults, nsid];
result = [self fetch: request];
return [result valueForKeyPath: #"photos.photo"];
}
Which does a fetch to the flickr API.
What is happening though is that is stuck in an eternal loop. Even with the for statement being less than the count, it still eternal loops. I have NSLog'd the count of the FlickR photos and it = 11.
This may have something to do with it, but whenever I press the button to take me to the table view controller, I get a HUGE lag, close to a minute, and nothing is being calculated (photo-wise) as I've done a count++
Thanks
let me understand this.. By the last line of your first block of code, I conclude that that is the uitableview dataSource method, cellForRowAtIndexPath.. what doesn't really makes sense.. you you have a fetch there, you have A loop inside a loop, that is setting many images (by download them) in one single imageView, and this is happening for all your visible cells at the same time. This will never work!
The solution is:
1 - remove this method from the cellForRow, this is not the place to request the images
2 - create another method that will fetch the content
3 - create a method that will do your loops and store the images on the array so you don't need to do that many times, only one..
4 - reload the tableview after you finish the step 3
5 - use the array of images that is already done to set your images by indexPath.row in your cell..
6 - I recommend you to use a Library for imageCache (i.e https://github.com/rs/SDWebImage)
NSArray *photos = [self.flickr photosForUser:#"James Kinvig"];
for (int i = 0; i < [[self.flickr photosForUser:#"James Kinvig"] count]; i++)
{
for (NSDictionary *dictionary in photos)
{
You have two nested loops iterating over the same collection. This turns what should be an O(n) operation into O(n^2) and explains why your process is taking a very long time.
Since the loop bodies never use i, I would fix it by getting rid of the outer loop:
NSArray *photos = [self.flickr photosForUser:#"James Kinvig"];
for (NSDictionary *dictionary in photos)
{

objective c - how to get elements dynamically from NSArray? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have the json file below and I save it in an array by doing
NSArray *allFrames = [self.campaign.form.gameParams objectForKey:#"frames"];
What I want to achieve, is to place all the horizontal images from the url in a view. My problem is I want to do this dynamically, because the number of the elements may change.
frames = (
{
horizontal = "http://mysite.co.uk/files/frames/5_h.png";
vertical = "http://mysite.co.uk/files/frames/5_v.png";
},
{
horizontal = "http://mysite.co.uk/files/frames/6_h.png";
vertical = "http://mysite.co.uk/files/frames/6_v.png";
}
{
horizontal = "http://mysite.co.uk/files/frames/6_h.png";
vertical = "http://mysite.co.uk/files/frames/6_v.png";
}
);
What I have done until now is to store the urls statically like this
NSDictionary *frames = allFrames[0];
NSString * link1 = [frames objectForKey:#"horizontal"];
NSDictionary *frames = allFrames[1];
NSString * link2 = [frames objectForKey:#"horizontal"];
NSDictionary *frames = allFrames[2];
NSString * link3 = [frames objectForKey:#"horizontal"];
And then convert the urls into images
UIImage *webImage1 = [UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:link1]]];
etc
Any ideas how to do it?
Something like this?
NSMutableArray *imageArray = [[NSMutableArray alloc] init];
for (int i = 0; i < allFrames.count; i++) {
NSDictionary *dict = allFrames[i];
NSString *link = dict[#"horizontal"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:link]]];
[imageArray addObject:image];
}
horizontal data can be filtered using valueForKey of NSArray.
NSArray *horizontalImages = [frames valueForKey:#"horizontal"];
for(NSString *urlString in horizontalImages)
{
UIImage *webImage1 = [UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString: urlString]]];
}
NSArray *dict = #[#{#"horizontal": #"http://mysite.co.uk/files/frames/5_h.png"}, #{#"horizontal": #"http://mysite.co.uk/files/frames/6_h.png"}, #{#"horizontal": #"http://mysite.co.uk/files/frames/6_h.png"}];
NSMutableArray *horizontalLinks = [NSMutableArray array];
[dict enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[horizontalLinks addObject:[obj objectForKey:#"horizontal"]];
}];
NSLog(#"Horizontal Links: %#", horizontalLinks);
Will give you:
Horizontal Links: (
"http://mysite.co.uk/files/frames/5_h.png",
"http://mysite.co.uk/files/frames/6_h.png",
"http://mysite.co.uk/files/frames/6_h.png"
)

working with variables from dispatch_async

I have a method which downloads some pics from a server. I had the buffer for the downloaded data defined withing async block (NSMutableArray *imgtmp) but haven't figured out how to get the array back out of there. What's the best way to access imgtmp in order to return it's contents, or set an instance variable from it?
I've been looking in the Apple Block docs but I must not be in the right section. Do I need to declare imgtmp using the __block keyword somehow? I tried that but imgtmp was still empty outside the block. Thanks!
EDIT: code updated with working model
- (void) loadImages
{
// temp array for downloaded images. If all downloads complete, load into the actual image data array for tablerows
__block NSMutableArray *imgtmp = [[NSMutableArray alloc] init];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
int error = 0;
int totalitems = 0;
NSMutableArray *picbuf = [[NSMutableArray alloc] init];
for (int i=0; i < _imageURLS.count;i++)
{
NSLog(#"loading image for main image holder at index %i",i);
NSURL *mynsurl = [[NSURL alloc] initWithString:[_imageURLS objectAtIndex:i]];
NSData *imgData = [NSData dataWithContentsOfURL:mynsurl];
UIImage *img = [UIImage imageWithData:imgData];
if (img)
{
[picbuf addObject:img];
totalitems++;
}
else
{
NSLog(#"error loading img from %#", [_imageURLS objectAtIndex:i]);
error++;
}
}// for int i...
dispatch_async(dispatch_get_main_queue(),
^{
NSLog(#"_loadedImages download COMPLETE");
imgtmp = picbuf;
[_tvStatus setText: [NSString stringWithFormat:#"%d objects have been retrieved", totalitems]];
NSLog (#"imgtmp contains %u images", [imgtmp count]);
});// get_main_queue
});// get_global_queue
}
You're hitting that "final" NSLog call before any of your block code has executed. All your image loading stuff is wrapped in a dispatch_async. It is executed asynchronously whereas the NSLog is called right away.
I think the best thing for you to do is to pass imgtmp along to some persistent object. Perhaps your view controller can have a property like:
#property (nonatomic, copy) NSArray *images;
and you can assign that in the same block where you assign the text to _tvStatus.

how to add NSString from loop in NSMutableArray

I want to add NSString Name in NSmutableArray but I dont know about that.
I get 5 NSString name from url in wamp server and I want add these names in NSMutableArray.
this is my code but do not work!!! :
NSMutableArray *file;
for (int j=0; j < 5; j++) {
NSString *fileName = [[NSString alloc]initWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://192.168.1.101/janatan/filemanager.php?dir=root&file=%d&name",j]]];
NSLog(#"%#",fileName);
[file addObject:fileName] //right???
}
You are not allocating NSMutableArray
NSMutableArray *file = [[NSMutableArray alloc] initWithCapacity:5];
If you are not sure about the number of elements gets added to array beforehand, you can use
NSMutableArray *file = [[NSMutableArray alloc] init];
Firstly, the NSMutableArray must be allocated.
Secondly, the using of magic numbers must be avoided.
Next, the readability of code should be improved by replace
NSString *fileName = [[NSString alloc]initWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://192.168.1.101/janatan/filemanager.php?dir=root&file=%d&name",j]]];
to something more convenient. Moreover, the memory here is allocated but never released.
Your code may be as follows:
const int numberOfFiles = 5;
NSMutableArray *file = [NSMutableArray array]
for(int i = 0; i < numberOfFiles; ++i){
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://192.168.1.101/janatan/filemanager.php?dir=root&file=%d&name", i]];
NSString *fileName = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
[file addObject:fileName];
}
But here are we can find some problems.
For example if url is changed on server-side you'd rewrite the code. If the number of elements is changed it's the same. So it would be good to find a way to avoid this kind of dependence.

iOS - App crash without error during loop process

during a loop process, my App crash without error. The array count is equal to 175260. With profiler I don't have leaks, so I don't know why the App exit, maybe the CPU usage 100% during a lot of time?
Thank you for your help.
Just this code following crash the App :
for(unsigned int i = 0; i <14;i++)
{
if(findSensor[i]==YES)
{
for(unsigned int j = 1; j <[array count];j++)
{
#autoreleasepool {
if([[[[array objectAtIndex:j] componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
//Code here
}
}
}
}
}
The full code is :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/%#",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase];
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:fileName];
NSFileHandle *output = [NSFileHandle fileHandleForReadingAtPath:[NSString stringWithFormat:#"%#/%#10",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase]];
if(output == nil)
{
NSManagedObjectContext *context = [self managedObjectContext];
_recordlocal = [NSEntityDescription insertNewObjectForEntityForName:#"RECORD" inManagedObjectContext:context];
_recordlocal.date = [ibNavSettings interfaceSettings].selectedFileToDataBase;
NSData *inputData = [NSData dataWithData:[fh readDataToEndOfFile]];
NSString *inputString = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
NSArray *array = [[NSArray alloc] initWithArray:[inputString componentsSeparatedByString:#"\n"]];
for(unsigned int i = 0; i <14;i++)
{
if(findSensor[i]==YES)
{
[[NSFileManager defaultManager] createFileAtPath:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)] contents:nil attributes:nil];
NSMutableString *saveString = [[NSMutableString alloc] init];
int count = 0;
for(unsigned int j = 1; j <[array count];j++)
{
#autoreleasepool {
if([[[[array objectAtIndex:j] componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
[saveString appendString:[array objectAtIndex:j]];
[saveString appendString:#"\n"];
if(i == 0)
count++;
progress++;
pourcent = progress/total;
load = pourcent*100;
if(load%5==0)
[self performSelectorInBackground:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
}
}
}
[saveString writeToFile:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)] atomically:YES encoding:NSUTF8StringEncoding error:nil];
if(i == 0)
_recordlocal.count = [[NSNumber alloc] initWithInt:(count/50)];
}
}
_recordlocal.load = [[NSNumber alloc] initWithBool:YES];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Core data error %#, %#", error, [error userInfo]);
abort();
}
I would guess that your app is crashing without a readable exception because it is running out of available RAM, especially since you indicated that it is running through a large number of iterations.
For a test, I would recommend doing what Rikkles suggests with the autorelease pool. In addition, since the value of i (and as a result the comparison string) rarely changes, I would create that string outside the j loop as well. This would avoid the creation of a lot of extra strings laying around.
Beyond that, since it appears that you are looking for a string at the beginning of a string that is delimited by a semicolon, I would recommend instead of doing componentsSeparatedByString and then examining element zero that you use the NSString method hasPrefix to check for the condition you are looking for.
Here is an example:
for(unsigned int i = 0; i <14;i++)
{
NSString *searchString = [NSString stringWithFormat:#"%d;", 10*(i+1)];
if(findSensor[i]==YES)
{
for(unsigned int j = 1; j <[array count];j++)
{
if([[array objectAtIndex:j] hasPrefix:searchString])
{
//Code here
}
}
}
}
(I hope this compiles and runs, if it doesn't it should require more than minor tweaks. I am away from my Mac right now.)
If this doesn't help, then something going on inside //Code here must be the culprit.
Why are you creating [array count] autoreleasepools? What's the point of creating so many of them? It could crash because of that. Put the #autoreleasepool outside the for loop.
The only reason I could think that you would do that is if you create so many transient objects inside each iteration of the for loop that you'd want to get rid of them as soon as you got out of the iteration. But there are other ways to do that, including reusing those objects within each iteration.
First suggestion
Just use fast enumeration for the inner loop, you aren't actually using the index 'j' for anything
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/Enumeration.html
Second suggestion
Put some NSLog's in place, it will slow everything down, but you need to figure out what point you are failing at. That will help point everyone in the right direction.
Third suggestion
Actually use NSError objects and output their value if an error is thrown:
NSError *writeError = nil;
[saveString writeToFile:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)]
atomically:YES
encoding:NSUTF8StringEncoding
error:&writeError];
if(error != nil) NSLog(#"error writing file: %#", [[writeError userInfo]description]);
Fourth suggestion
You appear to try to be updating the UI from a background thread. This will not work or will cause a crash. UI code can only be called from a main thread. So dont do this:
[self performSelectorInBackground:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
If you are already on a background thread this will probably crash because you are creating threads on threads on threads. You instead would want to call:
[self performSelectorOnMainThread:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
Fifth suggestion
You may be going over the maximum length for NSString (it's big but I did it once on accident before). You should probably just be appending the file on each iteration of the loop instead, so you don't have an ever growing NSMutableString:
NSString *path = [NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)]
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:filePath];
NSData *newLine = [#"\n" dataUsingEncoding:NSUTF8StringEncoding];
for(NSString *rowString in array)
{
if([[[rowString componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
NSData *stringData = [rowString dataUsingEncoding:NSUTF8StringEncoding];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:stringData];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:newLine];
if(i == 0)
count++;
progress++;
pourcent = progress/total;
load = pourcent*100;
if(load%5==0)
[self performSelectorOnMainThread:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
}
}
}
And this has the added benefit of helping you ditch the autoreleasepools
This was invalid
If your array does in fact have 175260 rows, that is probably your issue. You are looping using unsigned int as your index var. Unsigned ints in c only have a max value of 65535. Use an unsigned long int, max 4294967295.

Resources