NSArray release crashes app - objective-c

I am trying to "tokenize" my data that I get from my text file.
When I am doing this, I get an error like this:
malloc: * error for object 0x844c730: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
The code I use looks like this:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"mydata" ofType:#"txt"];
NSString *rawText = [NSString stringWithContentsOfFile:filePath encoding:NSASCIIStringEncoding error:nil];
//No error was caused by above line
NSArray *tmp = [rawText componentsSeparatedByString:#"####"];
NSString *title = #"";
NSString *detail = #"";
for(int i = 0; i < [tmp count]-1; i++)
{
NSArray *base = [[tmp objectAtIndex:i] componentsSeparatedByString:#"##"];
title = [[NSString alloc] initWithFormat:#"%#$$%#",title,[base objectAtIndex:0]];
detail = [[NSString alloc] initWithFormat:#"%#$$%# | %# | %#",
title,
[base objectAtIndex:0],
[base objectAtIndex:1],
[base objectAtIndex:2]
];
[base release];
}
[tmp release];
It must be this part of the code, since if I comment this piece out, it works fine.
Reading the error it says set a breakpoint which I have no idea to put that in malloc_error_break
What is wrong in my memory management doing?
Or else how can I split up the string in some other way?

You got tmp from componentsSeparatedByString:. Since that selector doesn't start with "alloc" or "new" or "copy" or "mutableCopy", and since you didn't do [tmp retain], you don't own tmp. So you shouldn't do [tmp release].
Same for base.

Base and temp are autorelease objects so you should not release that objects.

You don't have to release base. It's already autoreleased.

You are trying to release array base without allocating it.
NSArray *base = [[tmp objectAtIndex:i] componentsSeparatedByString:#"##"]; didn't allocate memory for base.You don't need [base release];
until it is allocated.

Related

How to convert NSUrl to NSString?

After AVAssetExportSession has complete export video.
I have plan to garb Video Path to upload via Youtube.
but [GDataUtilities MIMETypeForFileAtPath:path defaultMIMEType:#"video/mp4"];
it only accept NSString.
Is it possible to convert NSUrl in to NSString for video file path.
i have try to use NSString *path = [ExportoutputURL absoluteString];
but it crash.
Here is the Code
- (void)exportDidFinish:(AVAssetExportSession*)session {
ExportoutputURL = session.outputURL;
_exporting = NO;
NSIndexPath *exportCellIndexPath = [NSIndexPath indexPathForRow:2 inSection:kProjectSection];
ExportCell *cell = (ExportCell*)[self.tableView cellForRowAtIndexPath:exportCellIndexPath];
cell.progressView.progress = 1.0;
[cell setProgressViewHidden:YES animated:YES];
[self updateCell:cell forRowAtIndexPath:exportCellIndexPath];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:ExportoutputURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:ExportoutputURL
completionBlock:^(NSURL *assetURL, NSError *error){
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"writeVideoToAssestsLibrary failed: %#", error);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription]
message:[error localizedRecoverySuggestion]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
else {
_showSavedVideoToAssestsLibrary = YES;
ExportCell *cell = (ExportCell*)[self.tableView cellForRowAtIndexPath:exportCellIndexPath];
[cell setDetailTextLabelHidden:NO animated:YES];
[self updateCell:cell forRowAtIndexPath:exportCellIndexPath];
NSArray *modes = [[[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, UITrackingRunLoopMode, nil] autorelease];
[self performSelector:#selector(hideCameraRollText) withObject:nil afterDelay:5.0 inModes:modes];
}
});
}];
}
[library release];
}
- (void)uploadVideoFile {
NSString *devKey = DEVELOPER_KEY;
GDataServiceGoogleYouTube *service = [self youTubeService];
[service setYouTubeDeveloperKey:devKey];
NSURL *url = [GDataServiceGoogleYouTube youTubeUploadURLForUserID:kGDataServiceDefaultUser];
// load the file data
NSString *path = [ExportoutputURL absoluteString];//[[NSBundle mainBundle] pathForResource:#"video_2451" ofType:#"mp4"];//[mFilePathField stringValue];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
NSString *filename = [path lastPathComponent];
// gather all the metadata needed for the mediaGroup
NSString *titleStr = #"Upload Test";//[mTitleField stringValue];
GDataMediaTitle *title = [GDataMediaTitle textConstructWithString:titleStr];
NSString *categoryStr = #"Entertainment";//[[mCategoryPopup selectedItem] representedObject];
GDataMediaCategory *category = [GDataMediaCategory mediaCategoryWithString:categoryStr];
[category setScheme:kGDataSchemeYouTubeCategory];
NSString *descStr = #"GData Description";//[mDescriptionField stringValue];
GDataMediaDescription *desc = [GDataMediaDescription textConstructWithString:descStr];
NSString *keywordsStr = #"RAGOpoR Demo";//[mKeywordsField stringValue];
GDataMediaKeywords *keywords = [GDataMediaKeywords keywordsWithString:keywordsStr];
BOOL isPrivate = NO;//([mPrivateCheckbox state] == NSOnState);
GDataYouTubeMediaGroup *mediaGroup = [GDataYouTubeMediaGroup mediaGroup];
[mediaGroup setMediaTitle:title];
[mediaGroup setMediaDescription:desc];
[mediaGroup addMediaCategory:category];
[mediaGroup setMediaKeywords:keywords];
[mediaGroup setIsPrivate:isPrivate];
NSString *mimeType = [GDataUtilities MIMETypeForFileAtPath:path
defaultMIMEType:#"video/mp4"];
// create the upload entry with the mediaGroup and the file
GDataEntryYouTubeUpload *entry;
entry = [GDataEntryYouTubeUpload uploadEntryWithMediaGroup:mediaGroup
fileHandle:fileHandle
MIMEType:mimeType
slug:filename];
SEL progressSel = #selector(ticket:hasDeliveredByteCount:ofTotalByteCount:);
[service setServiceUploadProgressSelector:progressSel];
GDataServiceTicket *ticket;
ticket = [service fetchEntryByInsertingEntry:entry
forFeedURL:url
delegate:self
didFinishSelector:#selector(uploadTicket:finishedWithEntry:error:)];
[self setUploadTicket:ticket];
GTMHTTPUploadFetcher *uploadFetcher = (GTMHTTPUploadFetcher *)[ticket objectFetcher];
}
Error EXC_BAD_ACCESS at
NSString *path = [ExportoutputURL absoluteString];
Is it possible to convert NSUrl in to NSString for video file path.
Yes. Send it an absoluteString message.
i have try to use NSString *path = [ExportoutputURL absoluteString]; but it crash.
If you want a path, send the URL a path message. A string representing a URL is generally not a valid path; if you want a path, ask it for one.
As for the crash, it does not mean absoluteString is wrong. Sending absoluteString to an NSURL object is the correct way to get an NSString object that represents the URL. The problem is somewhere else.
Error EXC_BAD_ACCESS at
NSString *path = [ExportoutputURL absoluteString];
This probably means that ExportoutputURL points to something that is not nil but is also not a valid object. It might have pointed to an NSURL object at some point, but it doesn't now.
My guess would be that the problem is this:
ExportoutputURL = session.outputURL;
You assign the URL to the ExportoutputURL instance variable, but you don't retain the object or make your own copy. Therefore, you don't own this object, which means you are not keeping it alive. It may die at any time, most probably after this method (exportDidFinish:) returns.
The crash is because you call uploadVideoFile later, after the URL object has already died. You still have a pointer to it, but that object no longer exists, so sending a message to it—any message—causes a crash.
There are three simple solutions:
Retain the URL object when you assign it to your instance variable.
Make your own copy of the URL object and assign that to the instance variable.
Declare ExportoutputURL as a property, with either the strong keyword or the copy keyword, and assign the object to the property, not the instance variable. That will call the property's setter, which, if you synthesize it or implement it correctly, will retain or copy the URL for you.
Either way, you will own the object, and that will keep it alive until you release it. Accordingly, you will need to release it when you are done with it (in dealloc, if not earlier), so that you don't leak it.
This all assumes that you are not using ARC. If you are using Xcode 4.2 or later, and can require iOS 4 or later, you should migrate your project to ARC, as it makes so many things much simpler. You would not need to retain or copy this object if you were using ARC, which means that migrating to ARC now is a fourth solution (but certainly a larger-scale one).
Use either absolutePath or path as mentioned by Miek and Nepster. Expanding on their answers, the difference between lies in the prefix.
NSString* string1 = [url absoluteString]; // #"file:///Users/jackbrown/Music/song name.mp3"
NSString* string2 = [url path]; // #"/Users/jackbrown/Music/song name.mp3"`
NSString *path = [[NSString alloc] initWithString:[url path]]; ?
Use this. I think it will help you.
In Objective c
NSString *testString = testUrl.absoluteString;
In Swift
var testString : String = testUrl.absoluteString
Simply you can do it like this.
NSString *myString = [myURL absoluteString];

Objective C - NSString - Memory basics

I am trying to return an NSString that has been initialized from a plist.
If I comment out my release lines this code works. I would however like to release these objects from memory as I no longer need them.
I thought that 'initWithString' would copy the contents of the target string into my new string meaning I could safely release the NSMutableArray. But it isn't. Why not?
+ (NSString*) genImage {
NSString *path = [[NSBundle mainBundle] pathForResource:
#"Images" ofType:#"plist"];
NSMutableArray *arrayOfImages = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSLog(#"%d", [arrayOfImages count]);
int indexToLoad = 0;
NSString *res = [[NSString alloc] initWithString:[arrayOfImages objectAtIndex:indexToLoad] ];
[arrayOfImages release];
[path release];
return res;
}
You do not retain the return value of -[NSBundle pathForResource:ofType:] (the path variable), so there is no need to release it (and doing so will cause a crash, most likely). However, you should autorelease res, as you do retain that. You can change your last line to
return [res autorelease];

iPad app crashing with no crash log while reading from file

The basic structure of my program has the user select an item from a UITableView, which corresponds to a stored text file. The file is then read into an array and a dictionary, where the array has the keys (I know I can just get the keys from the dictionary itself, this isn't my question).
The view is then changed to a UISplitView where the master view has the keys, and the detail view has the items in the dictionary attributed to that key. In this case, it's a series of "Yes/No" questions that the user selects the answer to.
My problem is this: When I click on a cell in the UITableView (first screen), it works fine, the data is read in perfectly, and so on. When I go back to the UITableView and click on the same cell again, the program crashes. Here is the read-in-from-file method:
-(NSArray *)readFromFile:(NSString *)filePath{
// NSLog(#"Path was: %#", filePath);
NSString *file = [[NSString alloc] initWithContentsOfFile:filePath];
// NSLog(#"File was: %#", file);
NSScanner *fileScanner = [[NSScanner alloc] initWithString:file];
NSString *held;
NSString *key;
NSMutableArray *detailStrings;
NSMutableArray *keys = [[NSMutableArray alloc] init];
NSMutableDictionary *details = [[NSMutableDictionary alloc] init];
/**
This is where the fun stuff happens!
**/
while(![fileScanner isAtEnd]){
//Scan the string into held
[fileScanner scanUpToString:#"\r" intoString:&held];
NSLog(#"Inside the while loop");
// If it is a character, it's one of the Key points, so we do the management necessary
if ([[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:[[held lowercaseString] characterAtIndex: 0]]){
NSArray *checkers = [[NSArray alloc] initWithArray:[held componentsSeparatedByString:#"\t"]];
NSLog(#"Word at index 2: %#", [checkers objectAtIndex:2]);
if(detailStrings != nil){
[details setObject:detailStrings forKey:key];
[detailStrings release];
}
NSLog(#"After if statement");
key = [checkers objectAtIndex:2];
[keys addObject:(NSString *) key];
detailStrings = [[NSMutableArray alloc] init];
}
else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[[held lowercaseString] characterAtIndex: 0]]){
NSArray *checkers = [[NSArray alloc] initWithArray:[held componentsSeparatedByString:#"\t"]];
NSLog(#"Word at index 1: %#", [checkers objectAtIndex:1]);
[detailStrings addObject:[checkers objectAtIndex:1]];
}
}
NSLog(#"File has been read in");
[details setObject:detailStrings forKey:key];
NSArray *contents = [[NSArray alloc] initWithObjects:(NSMutableArray *) keys, (NSMutableDictionary *) details, nil];
[detailStrings release];
return contents;
}
I've determined that the program crashes inside the
if(detailStrings != nil)
statement. I figure this is because I'm missing some memory management that I am supposed to be doing, but don't have the knowledge of where it's going wrong. Any ideas as to the problem, or why it is crashing without giving me a log?
detailStrings is not initialized when you enter the while loop. When you declare NSMutableArray *detailStrings; inside a method, detailStrings is not automatically set to nil. So when you do
if ( detailStrings != nil ) { .. }
it enters the if statement and since it is not initialized, it will crash when you access detailStrings.
Another thing is that detailStrings won't be initialized if it enters the else part of the loop first. That will cause a crash too. So based on your requirement, either do
NSMutableArray *detailStrings = nil;
or initialize it before you enter the while loop.
Deepak said truth. You should initialize detailStrings with nil first.
But there is second possible issue:
I recommend also to set nil after release, because in the next loop you may test nonexistent part of memory with nil.
if(detailStrings != nil){
[details setObject:detailStrings forKey:key];
[detailStrings release];
detailStrings = nil;
}
And the third possible issue: depending from incoming data you may go to the second part of IF statement first time and try to addObject into non-initialized array.
The fourth (hope last): you have memory leak with "checkers" arrays
Here's what I'm seeing:
//read in the file
NSString *file = [[NSString alloc] initWithContentsOfFile:filePath];
//create the scanner
NSScanner *fileScanner = [[NSScanner alloc] initWithString:file];
//declare some uninitialized stuff
NSString *held;
NSString *key;
NSMutableArray *detailStrings;
//initialize some stuff
NSMutableArray *keys = [[NSMutableArray alloc] init];
NSMutableDictionary *details = [[NSMutableDictionary alloc] init];
//begin loop
while(![fileScanner isAtEnd]){
//scan up to a newline
[fileScanner scanUpToString:#"\r" intoString:&held];
//see if you scanned a lowercase string
if ([[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:[[held lowercaseString] characterAtIndex: 0]]){
//make an array
NSArray *checkers = [[NSArray alloc] initWithArray:[held componentsSeparatedByString:#"\t"]];
//do a check... against an uninitialized value
if(detailStrings != nil){
//set a potentially uninitialized value into an array with an uninitialized key
[details setObject:detailStrings forKey:key];
At this point, you're pretty much hosed.
The fix:
properly initialize your variables
run the static analyzer
read the memory management programming guide

NSString EXC_BAD_ACCESS

I'm stuck with the following bit of code.
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
When I log gridRef, it displays the correct result. However, the line setting answerLabel.text causes an EXC_BAD_ACCESS error and the program crashes. IB is connected to the correct label, what is the problem?
Thanks
I've updated the code as follows:
- (IBAction)convertLatLong {
NSArray *latLong = [[NSArray alloc] initWithObjects: latTextField.text, longTextField.text, nil];
GridRefsConverter *converter = [[GridRefsConverter alloc] init];
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
NSLog(#"Label: %#", self.answerLabel.text);
answerLabel.text = #"Yippy";
self.answerLabel.text = gridRef;
[gridRef release];
[converter release];
[latLong release];
}
answerLabel is initialised through #property #synthesize when the view controller is pushed onto the stack. (I don't know how it gets init'd apart from it's one of the magical things IB does for you. Or so I assume. I've used exactly the same method in other view controllers and have not had this issue.
I've found the culprits - the question is, how do I go about releasing them?
NSString *eString = [[NSString alloc] initWithFormat: #"%f", e];
NSString *nString = [[NSString alloc] initWithFormat: #"%f", n];
eString = [eString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
nString = [nString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
NSString *theGridRef = [letterPair stringByAppendingString: eString];
theGridRef = [theGridRef stringByAppendingString: nString];
[eString release];
[nString release];
return theGridRef;
and:
NSArray *gridRef = [[NSArray alloc] init];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: E]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: N]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithInteger: 8]];
NSString *theGridRef = [[NSString alloc] initWithFormat: #"%#", [self gridRefNumberToLetter: gridRef]];
[gridRef release];
[theGridRef autorelease];
return theGridRef;
}
You should enable zombie detection by setting the environment variable NSZombieEnabled to YES, so you can see which object causes the bad access (don't forget to remove this again when you found the bug).
Also you can use Instruments to find the location where the object actually gets released. For this start a new Instruments session and use the "Allocations" instrument. In the instrument settings check "Enable NSZombie detection" and "Record reference counts". When running the session you will break where the error occurs and you see a record of all retains/releases.
One place where you can have a quick look if your object is incorrectly freed is in the -viewDidUnload method, where you should release the outlet and set it to nil. If you forget the latter and you access the outlet somehow, it will result in a EXC_BAD_ACCESS.
Edited to match your update:
The problem is that you are assigning eString (and nString) a new string which was alloc/init-ed. Then you override those in the next statements, because -stringByPaddingToLength: (as well as all the other -stringBy... methods) return a new and autoreleased string object. So you lost the reference to the old string which means that there is a memory leak. Additionally at the end you release the already autoreleased objects explicitly which causes your bad access.
Instead you should create autoreleased strings from the beginning ([NSString stringWithFormat:...]) and don't release them at the end.
Check if asnwerLabel is actually non-null. You should also change this line:
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
To:
self.answerLabel.text = [NSString stringWithFormat: #"%#", gridRef];
Otherwise, you will end up with a memory leak in that line.
Maybe the label is not inited at that point in your code, try to check it. Why are you allocating a new NSString?
Just do:
self.label.text = gridRef;
[gridRef release];
how is answerLabel created? You might need to retain that. Or you possibly need to release some things (gridRef)?
I can't see any other issues with your code.
You can (and probably should) set your
answerLabel.text = gridRef;
gridRef is already an NSString, so you don't need to alloc it again.
EXC_BAD_ACCESS is usually a memory thing related to your retain/release count not balancing (or in my extensive experience of it :p).
Okay, the problem was trying to release NSStrings, so I've stopped doing that and the problem has been solved.
Can someone clarify how strings are retained and released. I was of the impression that:
string = #"My String"; is autoreleased.
NSString *string = [[NSString alloc] init...]; is not autoreleased and needs to be done manually.

NSMutableDictionary Memory management

please go through the following code and please explain why is it crashes in last line?
NSMutableDictionary *dic1 = [[NSMutableDictionary alloc] initWithCapacity:10];
NSString *val = [[NSString alloc] initWithFormat:#"Deepak"];
NSString *key = [[NSString alloc] initWithFormat:#"First Name"];
int a = [val retainCount];
a = [key retainCount];
[dic1 setObject:val forKey:key];
a = [val retainCount];
a = [key retainCount];
//self.mainDic = [dic1 copy];
self.mainDic = [dic1 mutableCopy];//mainDic is like #property(copy) NSMutableDictionary *
[self.mainDic setObject:#"Hi" forKey:#"Good"];//Problem
Thanks.
copy properties are not suitable for mutable classes as they don't respect mutability and just send the copy message to the instances - what happens in the second last line is basically:
// ... release previous mainDic, if any
mainDic = [[dic1 mutableCopy] copy];
As the copy message results in an immutable version, NSDictionary, you are probably getting exception for an unrecognized selector -setObject:forKey: while debugging.
If you want to use copy properties you should provide your own setter instead and remove that manual mutableCopy - see e.g. Apples docs on the copy semantics.
I've almost never used a property with a modifier other than (nonatomic, retain)
this has saved me of a lot of problems,
that and on the dealloc method set'em to nil
this is the way I'd do it
NSMutableDictionary *dic1 = [[NSMutableDictionary alloc] initWithCapacity:10];
NSString *val = [[NSString alloc] initWithFormat:#"Deepak"];
NSString *key = [[NSString alloc] initWithFormat:#"First Name"];
[dic1 setObject:val forKey:key];
self.mainDic = dic1; //where mainDic is like #property (nonatomic, retain) NSMutableDictionary *
[self.mainDic setObject:#"Hi" forKey:#"Good"];
//let's clean this mess up
[val release];
[key release];
[dic1 release];
#Deepak ! your code itself works well. And there is no static analysis erro except un-referring a and memory leaking of dic1.
self.mainDic = [dic1 mutableCopy]; works well. Please run your code block again.
I created a new project and put your code and tested it. There was no problem.
#Georg Fritzsche your code is making crash.