Problem with leaking NSStrings - iphone

My code leaks but I do not know exactly what am I doing wrong. Simply I have a function that takes array with NSStrings and outputs NSString formatted as CSV.
Here is my code:
-(NSString*)generateCSVfromArray: (NSMutableArray*) reportEntries {
NSString* accumulator = [NSString stringWithString:#""];
for (NSString* string in reportEntries) {
NSString* temp = [accumulator stringByAppendingString:string];
accumulator = temp;
if (![string isEqualToString:#"\n"]) {
NSString* temp = [accumulator stringByAppendingString:#";"];
accumulator = temp;
}
}
return accumulator;
}
When I check leaks in Instruments it turns out that many string objects leaked. I managed to isolate the problem to the method above. Can you please help me and point what am I doing wrong?

I don't believe you're leaking any strings in this method. Why do you think this is the method to blame? Remember that Instruments will tell you where the object was created, but this has little to do with where it is leaked. Run the Static Analyzer for more help with that (Cmd-Shift-A).
This method is wildly inefficient, though. You're creating a ton of temporary strings. You could write this much more efficiently like this:
-(NSString*)generateCSVfromArray:(NSArray*)reportEntries {
NSMutableString* accumulator = [NSMutableString string];
for (NSString* string in reportEntries) {
[accumulator appendString:string];
if (![string isEqualToString:#"\n"]) {
[accumulator appendString:#";"];
}
}
return accumulator;
}
There are of course very good CSV writers already available. Search for "Cocoa CSV." But I assume you want this specialized algorithm.

Related

Accessing C arrays(int[], float[], etc..) using Objective-C runtime

I'm trying to save all the variables in my class into NSUserDefaults using objc/runtime. And below is the code I'm using.
NSUInteger count;
Ivar *iVars = class_copyIvarList([self class], &count);
for (NSUInteger i=0; i<count; i++)
{
Ivar var = iVars[i];
NSString *varName = [NSString stringWithCString:ivar_getName(var) encoding:NSUTF8StringEncoding];
NSString *varType = [NSString stringWithCString:ivar_getTypeEncoding(var) encoding:NSUTF8StringEncoding];
if([varType hasPrefix:#"["])
{
NSLog(#"Array");
id var1 = [_manager valueForKey:varName];
NSLog(#"--- %#", var1);
NSData *data = [NSData dataWithBytes:&([_manager valueForKey:varName]) length:sizeof([_manager valueForKey:varName])]
[[NSUserDefaults standardUserDefaults] setObject:[_manager valueForKey:varName] forKey:varName];
}
else
{
NSLog(#"NonArray");
NSLog(#"--- %#", [_manager valueForKey:varName]);
[[NSUserDefaults standardUserDefaults] setObject:[_manager valueForKey:varName] forKey:varName];
}
}
free(iVars);
The problem is that, when there are only primitive datatypes, the above code works just fine. But, when I try to access a array variable like int[], or float[], it gets crashed with SIGABRT. it is not showing any other messages.
valueForKey doesn't return any values for C arrays.
If anybody know how to load values for C-arrays in runtime, please help.
Thanks in advance,
Suran
Unless you always provide a paired length method, your program will never know the length of the array returned. So... you will need to do some work someplace to accomplish this without a crash.
If I really wanted to do what you're doing, I would make the class itself create the array, providing NSData. If this is common, you may want to use a convention:
- (int*)pixelBuffer;
- (NSData *)pixelBufferForSerialization; // << returns a deep copy of
// self.pixelBuffer as an
// NSData instance.
So your above implementation would see that the property defines a scalar array, and then request NSData * data = obj.pixelBufferForSerialization; instead of trying to produce the data itself.
Update
It's best to let the class do it. Here's how to create NSData using such an array:
#interface DataManager : NSObject
{
#private
int* things;
size_t nThings;
}
- (int*)things;
- (NSData *)thingsAsNSData;
#end
#implementation DataManager
- (int*)things
{
return things;
}
- (NSData *)thingsAsNSData
{
// note: you may need to choose an endianness for serialization
if (0 == nThings) return [NSData data];w
return [NSData dataWithBytes:things length:nThings * sizeof(things[0])];
}
#end
Again - you want the class to create the data because it knows its own structure best.

Getting exception as "Collection was mutated while being enumerated"

I am getting the Collection was mutated while being enumerated exception when I am using this code can any one suggest me how to get out of this.
PaymentTerms * currentElement;
for (currentElement in termsArray)
{
printf("\n currentElement Value........%s",[currentElement.days UTF8String]);
printf("\n Str value...%s",[Str UTF8String]);
NSRange range = [currentElement.days rangeOfString:Str options:NSCaseInsensitiveSearch];
if(!(range.location != NSNotFound))
{
PaymentTerms *pTerm1 = [[PaymentTerms alloc]init];
pTerm1.days = Str;
printf("\n pTerm1.days...%s",[ pTerm1.days UTF8String]);
[termsArray addObject:pTerm1];
}
}
Hope I get quick response from ur side.
Thank in advance,
Monish.
You cannot change array while you're enumerating it. As a workaround you should accumulate new objects in temporary array and add them after enumeration:
PaymentTerms * currentElement;
NSMutableArray* tempArray = [NSMutableArray array];
for (currentElement in termsArray)
{
NSRange range = [currentElement.days rangeOfString:Str options:NSCaseInsensitiveSearch];
if(!(range.location != NSNotFound))
{
PaymentTerms *pTerm1 = [[PaymentTerms alloc]init];
pTerm1.days = Str;
[tempArray addObject:pTerm1];
[pTerm1 release];
}
}
[termsArray addObjectsFromArray: tempArray];
P.S. do not forget to release pTerm1 object you create - your code contains memory leak
In respond to poster's comment (and actual task) - I think the easiest way to make bool flag indicating if day value was found in cycle. If not - add new object after cycle ends:
PaymentTerms * currentElement;
BOOL dayFound = NO;
for (currentElement in termsArray)
{
NSRange range = [currentElement.days rangeOfString:Str options:NSCaseInsensitiveSearch];
if(range.location != NSNotFound)
dayFound = YES;
}
if (!dayFound)
// Create and add new object here
This line [termsArray addObject:pTerm1];
will throw that exception. You CANNOT add/delete an element from an array inside a for each loop. for (currentElement in termsArray)
Yes...we cannot enumerate while the array is getting updated...This might be irritating for the programmers who are from ActionScript background.Some times things go worse like "You even dont get a crash or intimation at runtime when you update an array count while it is being enumerated"-The execution just behaves abnormally at that time.
Btw you can go for this type of implementation where you can have minor changes to your code.
for (int i=0 ; i< termsArray.count ;i++) //counting termsArray on every iteration
{
id currentElement = [ termsArray objectAtIndex:i];
......
.....
}
Of-course,This(i< termsArray.count) might seem bad as we are calculating the count for every iteration...And thats the trick here to have minor changes.But I would strongly recommend VLADIMIR's implementation as its clear for reading.
you are adding an object to your collection as you are looping over it, thats whats causing the error. your if statement is nested inside the for
The error occurs because you are adding new objects to termsArray within the for loop
Create a new empty array (e.g.newTermsArray)
In the first loop create and add these new items to newTermsArray
Then you will need a second loop to add the items from newTermsArray back into the original termsArray
Just dealt with the same bug in a rather complex app. The solution is simple - create a copy and work with the copy of array (and remove it from the original if you want. Then discard the copy.
for(CharacterModelNode* node in self.allPlayers)
{
// Some operation that may mutate allPlayers (ex: player dies from poison damage and is removed from this array at some other part of an app)
}
//workaround in some cases - create a copy of array and run operations that can kill player on this array (it will not be mutated anywhere else in the app
NSArray* allPlayersCopy = [self.allPlayers copy];
for(CharacterModelNode* node in allPlayersCopy)
{
[node.character refreshBuffs];
}
use try and catch for handling exception in nsmutable array
#try {
//code for accessing element.
}
#catch (NSException *exception) {
/// show exception here
}

CGPDFDictionaryRef to something useful

A little bit of background:
I'm appending an image to an already existing pdf file. I found out that there isn't a way to just add a page to it, you must recreate the whole pdf, then remove the old one then move the new one. (If that is wrong please tell me, and it will save me a lot of headaches.) I've accomplished that part, but I'm now trying to copy of the Auxiliary Info (Title, Author, Keys, etc). Problem is there are so many type problems that the current method I'm using is to get the CGPDFDocumentRef's CGPDFDictionaryRef, then call CGPDFDictionaryApplierFunction. In the C function I pass it, I am extracting each key to an NSMutableDictionary so that I can then do something with the values and not have them locked in this terrible CGPDF format.
So basically my question would be:
Is there a better way of doing this? I've stared at so many documentation files I couldn't imagine missing anything, but really hope I have because the amount of workaround I'm having to do is getting absurd.
I've completed my workaround. I would still like to know if there is any better way to do this, so if you know of one or have any suggestions I'm willing to try them out.
Notes: I only check for bools, ints, strings, and arrays because according to Apple's documentation thats all that should be in the Auxiliary Information in a PDF. The array bit is untested, because I dont have a PDF with an array in the Aux Info. If someone would like to test that for me or link to a pdf with one in it I will gladly test it out.
First, in the class header create an id selfClass outside the #interface tags so that the C functions called by CGPDFDictionaryApplyFunction can access the current class. Also add an NSDictionary *auxInfo for storing the information. Once extracted, the NS type can be easily cast like this:
CFDictionaryRef newDictionary = (CFDictionaryRef)[self auxInfo];
I was actually done last night but thought I had to do another round of looping to convert from NS to CF, forgetting they were token-free bridged. So there you have it, hope everyone benefits from my labors. Ask questions if you need clarification. And once again, if there is an easier way to do this, if only an optimization of my code, please say so. I know this isn't a very elegant way to do this but it works for now.
- (void)extractPDFDictionary:(CGPDFDocumentRef)pdf{
NSLog(#"extractingPDFDictionary");
CGPDFDictionaryRef oldDict = CGPDFDocumentGetInfo(pdf);
CGPDFDictionaryApplyFunction(oldDict, copyDictionaryValues, NULL);
}
void copyDictionaryValues (const char *key, CGPDFObjectRef object, void *info) {
NSLog(#"key: %s", key);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch (type) {
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *tempStr = (NSString *)CGPDFStringCopyTextString(objectString);
[[selfClass auxInfo] setObject:tempStr
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
[tempStr release];
NSLog(#"set string value");
}
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
[[selfClass auxInfo] setObject:[NSNumber numberWithInt:objectInteger]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
NSLog(#"set int value");
}
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
[[selfClass auxInfo] setObject:[NSNumber numberWithBool:objectBool]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
NSLog(#"set boolean value");
}
}
case kCGPDFObjectTypeArray : {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *tempArr = [selfClass copyPDFArray:objectArray];
[[selfClass auxInfo] setObject:tempArr
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
[tempArr release];
NSLog(#"set array value");
}
}
}
}
- (NSArray *)copyPDFArray:(CGPDFArrayRef)arr{
int i = 0;
NSMutableArray *temp = [[NSMutableArray alloc] init];
for(i=0; i<CGPDFArrayGetCount(arr); i++){
CGPDFObjectRef object;
CGPDFArrayGetObject(arr, i, &object);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch(type){
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *tempStr = (NSString *)CGPDFStringCopyTextString(objectString);
[temp addObject:tempStr];
[tempStr release];
}
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
[temp addObject:[NSNumber numberWithInt:objectInteger]];
}
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
[temp addObject:[NSNumber numberWithBool:objectBool]];
}
}
case kCGPDFObjectTypeArray : {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *tempArr = [selfClass copyPDFArray:objectArray];
[temp addObject:tempArr];
[tempArr release];
}
}
}
}
return temp;
}
You might accomplish what you are describing by drawing into CGPDFContext, but if you have a complex PDF, you may be attempting something beyond what the API was intended for. You might take a look at section 3.4 of the PDF spec and see what you're getting into.

method with 2 return values

I want to call a method which returns two values
basically lets say my method is like the below (want to return 2 values)
NSString* myfunc
{
NSString *myString = #"MYDATA";
NSString *myString2 = #"MYDATA2";
return myString;
return myString2;
}
So when i call it, i would use??
NSString* Value1 = [self myfunc:mystring];
NSString* Value2 = [self myfunc:mystring2];
I guess im doing something wrong with it, can anyone help me out?
Thanks
You can only return 1 value. That value can be a struct or an object or a simple type. If you return a struct or object it can contain multiple values.
The other way to return multiple values is with out parameters. Pass by reference or pointer in C.
Here is a code snippet showing how you could return a struct containing two NSStrings:
typedef struct {
NSString* str1;
NSString* str2;
} TwoStrings;
TwoStrings myfunc(void) {
TwoStrings result;
result.str1 = #"data";
result.str2 = #"more";
return result;
}
And call it like this:
TwoStrings twoStrs = myfunc();
NSLog(#"str1 = %#, str2 = %#", twoStrs.str1, twoStrs.str2);
You need to be careful with memory management when returning pointers even if they are wrapped inside a struct. In Objective-C the convention is that functions return autoreleased objects (unless the method name starts with create/new/alloc/copy).
You have a few options:
NSArray: Just return an array. Pretty simple.
Pointers: Pass in two pointers, and write to them instead of returning anything. Make sure to check for NULL!
Structure: Create a struct that has two fields, one for each thing you want to return, and return one of that struct.
Object: Same a structure, but create a full NSObject subclass.
NSDictionary: Similar to NSArray, but removes the need to use magic ordering of the values.
As you can only return one value/object, maybe wrap them up in an array:
-(NSArray*) arrayFromMyFunc
{
NSString *myString = #"MYDATA";
NSString *myString2 = #"MYDATA2";
return [NSArray arrayWithObjects:myString,myString2,nil];
}
You can then use it like this:
NSArray *arr = [self arrayFromMyFunc];
NSString *value1 = [arr objectAtIndex:0];
NSString *value2 = [arr objectAtIndex:1];
You could pass results back by reference, but this is easy to get wrong (syntactically, semantically, and from memory management point of view).
Edit One more thing: Make sure that you really need two return values. If they are quite independent, two separate function are often the better choice - better reusabilty and mentainable. Just in case you are making this as a matter of premature optimization. :-)
You can only directly return one value from a function. But there is a way of doing it.
-(void) myfuncWithVal1:(NSString**)val1 andVal2:(NSString**)val2
{
*val1 = #"MYDATA";
*val2 = #"MYDATA2";
}
Then to call it outside the method you'd use:
NSString* a;
NSString* b;
[self myfuncWithVal1:&a andVal2:&b];
void myfunc(NSString **string1, NSString **string2)
{
*string1 = #"MYDATA";
*string2 = #"MYDATA2";
}
...
NSString *value1, *value2;
myfunc(&value1, &value2);
Remember that you need to pass a pointer to a pointer when working with strings and other objects.
Wrap the two strings in an NSArray:
- (NSArray*)myFunc
{
NSString *myString = #"MYDATA";
NSString *myString2 = #"MYDATA2";
return [NSArray arrayWithObjects:myString, myString2, nil];
}
NSArray *theArray = [self myFunc]
NSString *value1 = [theArray objectAtIndex:0];
NSString *value2 = [theArray] objectAtIndex:1];
I see everyone has mentioned an NSArray but I'd go with an NSDictionary so the values don't have to be added in order or even at all. This means it is able to handle a situation where you only want to return the second string.
- (NSDictionary*)myFunction {
NSString *myString1 = #"string1";
NSString *myString2 = #"string2";
return [NSDictionary dictionaryWithObjectsAndKeys: myString1, #"key1", myString2, #"key2", nil];
}
NSDictionary *myDictionary = [self myFunction]
NSString *string1 = [myDictionary objectForKey:#"key1"];
NSString *string2 = [myDictionary objectForKey:#"key2"];

variable parameter function - EXC_BAD_ACCESS when calling [obj release];

I have the following method:
(void)makeString:(NSString *)str1,... {
va_list strings;
NSString *innerText = [[NSString alloc] init];
NSString *tmpStr = [[NSString alloc] init];
if (str1) {
va_start(strings, str1);
while (tmpStr = va_arg(strings, id)) {
innerText = [innerText stringByAppendingString:tmpStr];
}
label.text = [str1 stringByAppendingString:innerText];
}
[tmpStr release];
}
I will eventually get to Objective C Memory Management reading, where I'm sure I will find the answer to this - probably related to pointers and copying - , but for now, can anyone explain why if I add [innerText release]; as the last line of this function, i get an EXC_BAD_ACCESS error at runtime?
First, your code is erroneous.
As far as I can see you are only concatenating the strings to assign the result to label.text.
I assume that label is an ivar, so label.text = … ist legal. Then the following should do it:
- (void)makeString: (NSString *)str1, ...
{
if (str1) {
NSString *tmpStr;
va_list strings;
va_start(strings, str1);
while (tmpStr = va_arg(strings, id)) {
str1 = [str1 stringByAppendingString: tmpStr];
}
label.text = str1;
}
}
Some notes:
You should not release any input parameter unless your method is about releasing something.
As the first answer stated, you should not release the result of stringByAppendingString: unless
you have retained it before.
[Update]
I changed the answer because it contained an error. label.text = str1 should retain str1 of course (if it wants to keep it). Especially the calling code should not retain str1 unless it wants to keep it for itself.
stringByAppendingString returns an autoreleased string, which is replacing your original assignment. So your release is not needed. But you are leaking memory with the two allocs above.
You should probably use [NSString initWithCString:va_arg(strings, id)] to assign the tmpStr too.