Keep incresing memory allocation - ios

In my iOS app implemented for save videos from the web. It keeps increasing the memory usage when downloading videos. I have inspect using profile in xcode and saw some malloc getting increase per video.
I am not familiar with profile stuff. I have released the receivedData NSMUtableData variable.
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectry = [paths objectAtIndex:0];
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSString *filename = [NSString stringWithFormat:(#"video_%#.mp4"),videoURL];
[receivedData writeToFile:[documentsDirectry stringByAppendingPathComponent:filename ] atomically:YES];
receivedData = nil;
[receivedData release];
progress.hidden = YES;
}
App getting down its performance. How can i fix this issue.

You have not released receivedData. You have set the variable to nil before, so any message sent to receivedData goes to nirvana.
Also don't forget to handle cases when connectionDidFinishLoading is never called, you have to release it elsewhere too because of this.

Related

How to download, update and read an XML file within an iOS app?

I am new to developing iOS apps and I wish to enable my app to download and read an XML file that will update each time the app is opened.
This is what I have so far:
NSURL * url = #"http://192.168.100.161/UploadWhiteB/wh.txt";
NSData * data = [NSData dataWithContentsOfURL:url];
if (data != nil) {
NSLog(#"\nis not nil");
NSString *readdata = [[NSString alloc] initWithContentsOfURL:(NSData *)data ];
Maybe you can use this code in your AppDelegate didFinishLaunchingWithOptions method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *localPath = [path objectAtIndex:0]; //The apps document directory
NSString* fileURL = #"http://192.168.100.161/UploadWhiteB/wh.txt";
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
if (!(data==nil)) {
[data writeToFile:[localPath stringByAppendingPathComponent:#"wh.txt"] atomically:YES];
NSLog(#"Did download file: wh.txt");
}
}
Then you can get the data by using this code in another class:
-(NSString *) getDocumentDirectory {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [path objectAtIndex:0]; //The apps document directory
}
NSData *xmlData = [NSData dataWithContentsOfFile:[[self getDocumentDirectory] stringByAppendingPathComponent:#"wh.txt"]];
if(!(xmlData==nil)) {
//Do your stuff
}
The didFinishLaunchingWithOptions method will run every time the app starts and download (and replace) the file wh.txt from your server to the file wh.txt on your device (in your apps document folder).
Then if you wan't to read the file you can use the NSXMLParser. See:
How to Parse XML Files in Xcode and
NSXMLParser example
Does it solve your problem?
If not, provide more information and I will edit my answer.

Prevent an app crash due to a slow connection when retrieving a remote file

I am currently using a function in my app's didFinishLaunchingWithOptions that retrieves a file, saves it to the application directory.
I have found that when there is a weak connection the app will crash when this is happening. I read that there is a 20 second time limit Apple allows before crashing the app. Is this correct? If so, I believe this is causing my issue as the app works flawlessly with the exception of being on a very weak connection.
How could I modify my logic below to try and compensate for this?
- (void)writeJsonToFile
{
//applications Documents dirctory path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//live json data url
NSString *stringURL = #"http://link-to-my-data.json";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
//attempt to download live data
if (urlData)
{
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"data.json"];
[urlData writeToFile:filePath atomically:YES];
}
//copy data from initial package into the applications Documents folder
else
{
//file to write to
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"data.json"];
//file to copy from
NSString *json = [ [NSBundle mainBundle] pathForResource:#"data" ofType:#"json" inDirectory:#"html/data" ];
NSData *jsonData = [NSData dataWithContentsOfFile:json options:kNilOptions error:nil];
//write file to device
[jsonData writeToFile:filePath atomically:YES];
}
}
It's a very bad idea to run this sort of thing on the main thread: I assume you are - basically, you'll block the entire UI while you wait for the network operation to complete.
dataWithContentsOfURL is not a good idea for this sort of thing. It will be much better to use NSURLConnection or one of the wrapper libraries like AFNetworking, because you can handle cases like when the connection times out gracefully.
These libraries also have built-in methods to asynchronously download the data, which prevents the main UI thread from being locked.
When is this downloaded data needed?
Depending on the answer, maybe you can call the method inside a thread. This will prevent the main thread from blocking.
Even if the data is needed from the beginning, you can just create a loader and download the file in the background, then make the app active after the file is downloaded.
I think to be more independent from internal implementation of NSData *urlData = [NSData dataWithContentsOfURL:url]; you should implement you own download class based on NSURLConnection.
The links to read:
URL Loading System Programming Guide
NSURLConnection Class Reference
NSURLConnectionDelegate Protocol Reference
So you can catch all connection errors by your code and implement right behavior in this case.

ASIHTTPRequest Asynchronous Download not canceling

I am trying to cancel an Asynchronous Download using ASIHTTPRequest with no joy. I am downloading a movie to disk on the appearance of a ViewController. What I want is when the user click close to dismiss the view controller I want to cancel the download. Here is the code I have so far:
Making the request:
-(void)retrieveLatestFile{
NSURL *url = [NSURL URLWithString:[appDelegate.urlToLoad stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
ashiRequest = [[ASIHTTPRequest requestWithURL:url] retain];
[ashiRequest setUsername:#"Appidae"];
[ashiRequest setPassword:#"Dubstance1"];
[ashiRequest setDelegate:self];
[ashiRequest startAsynchronous];
[ashiRequest setDownloadProgressDelegate:progressView];
NSLog(#"Value: %f", [progressView progress]);
if ([progressView progress]==1) {
[self hideInfoPane];
}
//NSLog(#"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *tempPath = [NSString stringWithFormat:#"%#/download/%#/%#", documentsDirectory, appDelegate.languageFolder,appDelegate.filename];
NSLog(#"Path: %#", tempPath);
[ashiRequest setDownloadDestinationPath:tempPath];
}
Cancel the the downloading:
- (IBAction)closeDocDisplay:(id)sender {
// Cancels an asynchronous request
[ashiRequest clearDelegatesAndCancel];
[self dismissModalViewControllerAnimated:YES];
}
Everything is working, the download is fine and the movie plays properly but when I dismiss the View Controller the request is not canceled and my file is still downloading (network activity indicator spins on the status bar. Even the ASIHTTP delegate error doesn't get called
Try using
ashiRequest.delegate = nil;
[ashiRequest cancel];
This should work.
I figured out what was wrong. I was using Reachability to call my retrieveLatestFileMethod. That's fine if Reachability is being used just once in the app, maybe from the delegate. But I had a few instances of it so it keept calling the same functions and downloads.

NSMutableArray disappears after app kill or restart using NSKeyedArchiver

I have an app that is storing Task objects (custom class) inside of a NSMutableArray. The Task class conforms to the NSCoding and NSCopying protocols, and I have also implemented the encodeWithCoder and initWithCoder methods.
When the user adds a new task to an NSMutableArray list, I save the array using NSKeyedArchiver. The list populates a UITableView.
The data is being saved, and when I exit the app and reenter, the data is still there. When I use another app for a while and come back, the data is still there. However, when I "kill" the app in the multitasking task manage or restart the device, the data disappears. Here are some important code snippets:
#define kFilename #"epapsTasksFile"
.
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
- (void)viewDidLoad {
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
self.list = [[NSKeyedUnarchiver unarchiveObjectWithFile:kFilename] retain];
}
else {
self.list = [NSMutableArray arrayWithCapacity:1];
}
...
}
- (void)applicationWillResignActive:(NSNotification *)notification {
NSMutableArray *updatedList = [[NSMutableArray alloc] initWithArray:self.list];
[NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename];
}
Why is my app not saving the data when the app is "killed" or the device is restarted? Also, it may be interesting to note that when I restart the iPhone simulator, the data stays in place.
You need to save the data
([NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename];
in applicationWillTerminate delegate method as as well to save it on termination.
EDIT:
applicationWillTerminate is not gauranteed in IOS4.0 and above.
Best is to check the return status of archiveRootObject:toFile: and see if the data is stored properly. As you figured it out, it can be case with wrong file path.

Gamekit and iPhone

I have my app set up to send a custom level in a form of a array to another person during a p2p connection. The receiving device saves the array to file for later use. I set up gamekit in my application, it will successfully search and connect to another device without any problems. Though a problem arises when I send data to a device, the receiving device will receive the data (and save the custom level like it should) but it will immediately crash afterwords.
Here are my methods that I use to send and receive data.
-(void) sendDataToPeers:(NSData *) data
{
if (currentSession)
{
//send the data
[self.currentSession sendDataToAllPeers:data withDataMode:GKSendDataReliable error:nil];
//Alerting the user that the custom level has been sent.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Sent!" message:#"Your custom level has been sent." delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
}
}
-(void) btnSend
{
//Data that will be sent
NSMutableData *theData = [NSMutableData data];
//Archiver
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData];
//Desired level to send
int theLevel =[[CTManager sharedInstance]getLevel];
//Path to the custom levels
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
NSString *customLevelsSen = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat:#"customLevels"]];
//Custom levels array
NSArray *theLevels = [[NSArray alloc] initWithContentsOfFile: customLevelsSen];
//Gets the desired level array from array of custom levels
NSArray *myArray = [[NSArray alloc]initWithArray:[theLevels objectAtIndex:theLevel-51]];
//prepare data
[archiver encodeObject:myArray forKey:#"level"];
[archiver finishEncoding];
//send the data
[self sendDataToPeers:theData];
//cleanup
[archiver release];
[theLevels release];
[myArray release];
}
-(void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context
{
//Archiver
NSKeyedUnarchiver *archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
//Gets the custom level in form of an array from data.
NSArray *level = [archiver decodeObjectForKey:#"level"];
[archiver finishDecoding];
[archiver release];
//Path to the array of custom levels
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
NSString *customLevelsRec = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat:#"customLevels"]];
//Gets the array of custom levels
NSMutableArray *customLevelArray = [[NSMutableArray alloc] initWithContentsOfFile:customLevelsRec];
//Adds a new array to the array of custom levels
[customLevelArray addObject:level];
//Saves the array.
[customLevelArray writeToFile:customLevelsRec atomically:YES];
//cleanup
[customLevelArray release];
//Message saying a custom level has been recieved
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Received!" message:#"A custom level has been saved." delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
}
Testing this has been a pain since I don't have two devices of my own currently, so I send a beta build to my friend who inturns tests them (he has ipod and iphone). Any help with this is appreciated...
If I can't find the problem I will most likely send the entire xcode project to him and via screen share work with the project on his computer to efficiently build and test the application. And I will be able to use debug mode.
I don't know if you ever found an answer to this question or not, I hope you did. But if you didn't I highly recommend that you try the new features of the new SDK. Instead of going through the whole encode/decode process, they have made it simple by having you do the following (in your send methodology):
data = [NSKeyedArchiver archivedDataWithRootObject:anObject];
where anObject can be pretty much any object, array, dictionary, whatever...
In your receive methodology:
NSObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:data];
where object can also be pretty much any object.
As far as the crash you are experiencing, have you verified on which line the crash occurs? Are you sure it happens in the code you posted? Or is it happening somewhere else?
I don't see anything wrong in your receiveData method.
Have you checked that the folder where you are trying to save the data (customLevels) exists?
I have succeeded to connect via an application using GameKit a device and the iPhone simulator. It's really handy to debug.
I haven't check if it was by bluetooth or wifi.
NSData* data = [#"TEXT" dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
[self.session sendData:data toPeers:peerID withDataMode:GKSendDataReliable error:&error];
(void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context {
NSString* message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];//#"TEXT"
NSString* nameOfTheTransmitter = [session displayNameForPeer:peer];// name who sent
}

Resources