Strange __block storage variable crash - objective-c

I have a problem in my code which I have distilled down to the following (silly) example
NSArray *array = [NSArray arrayWithObjects:#"1", #"2", #"3", nil];
__block NSString *a = #"-1";
[array enumerateObjectsUsingBlock:^(id whoCares, NSUInteger idx, BOOL *stop) {
a = [NSString stringWithFormat:#"%# %d", a, idx];
NSLog(#"%#", a);
}];
NSLog(#"%#", a);
This code works, but if I comment out the first NSLog (within the block) the code crashes. But, if I change the format string to the following
a = [NSString stringWithFormat:#"%d", idx];
then the code runs fine without the NSLog within the block.
What is going on here? I hope I am just misunderstanding something.

stringWithFormat: gives you an autoreleased object, which you're not retaining. By the time the block exits and you call NSLog, a might have already been deallocated.
One solution might be to use a mutable string and append to it each time instead of reassigning.
NSArray *array = [NSArray arrayWithObjects:#"1", #"2", #"3", nil];
NSMutableString *a = [NSMutableString stringWithFormat:#"-1"];
[array enumerateObjectsUsingBlock:^(id whoCares, NSUInteger idx, BOOL *stop) {
[a appendFormat:#" %d", idx];
}];
NSLog(#"%#", a);

Related

[mutableArray initWithArray: [str componentsSeparatedByString:#" "]] compile is right,but encounter error while running

[mutableArray initWithArray: [str componentsSeparatedByString:#" "]];
componentsSeparatedByString returns a NSArray *, and initWithArray accepts an NSArray *.
However, when I run this code there is some error. What's the issue?
You forgot the call to alloc:
// Assuming:
NSMutableArray *mutableArray;
// Then:
mutableArray = [[NSMutableArray alloc] initWithArray:[str componentsSeparatedByString:#" "]];
Alternatively:
mutableArray = [[str componentsSeparatedByString:#" "] mutableCopy];

NSMutableSet respondsToSelector addObject: returning false

I'm having a problem with respondsToSelector with NSMutableSet.
I have a program like this:
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
theClass = [arguments objectAtIndex: 1];
theMethod = [arguments objectAtIndex: 2];
theArgument = [arguments objectAtIndex: 3];
id object = [[NSClassFromString(theClass) init] autorelease];
if([object respondsToSelector:NSSelectorFromString(theMethod)]) {
NSLog(#"Result: %#",
[object performSelector:NSSelectorFromString(theMethod) withObject: theArgument]);
} else {
NSLog(#"Class %# doesn't respond to %#.",
theClass, theMethod);
}
I call it using ./program NSMutableSet addObject: str, but the program always says that NSMutableSet doesn't respond to addObject:.
I don't know why respondsToSelector always says that NSMutableSet doesn't respond to addObject. It's the same with ./program NSMutableSet allObjects.
You were checking something completely different...
[NSClassFromString(theClass) init]
needs to be
[[NSClassFromString(theClass) alloc] init]
instead.

How can I convert the values is an NSDictionary into an NSString?

I have a method in Objective-c that takes in an NSDictionary and returns the values as a space-separated NSString. This application is cross-platform, and as such I cannot use fast enumeration. This is what I have so far, followed by the output (which shows that the String is never created):
-(NSString *)stringValuesFromDict:(NSDictionary *map)
{
NSArray *values = [map allValues];
NSString *params = [NSString string];
NSLog(#"values length: %d", [values count]);
NSLog(#"values = %#", [values description]);
for (int i = 0; i < [values count]; i++)
{
[params stringByAppendingString:[values objectAtIndex:i]];
[params stringByAppendingString:#" "];
}
NSLog(#"params = %#", params);
return params;
}
The NSDictionary:
{"arg1"="monkey"}
The output:
values length: 1
values = (
monkey
)
params =
What am I doing wrong? How can I get params to be set to monkey?
If I read the question correctly, all you need is
[[dict allValues] componentsJoinedByString:#" "]
You need a mutable string. Change this:
NSString *params = [NSString string];
to this:
NSMutableString *params = [NSMutableString string];
Then change these:
[params stringByAppendingString:[values objectAtIndex:i]];
[params stringByAppendingString:#" "];
to these:
[params appendString:[values objectAtIndex:i]];
[params appendString:#" "];
stringByAppendingString: returns a new string. So you would have to do something like this:
params = [params stringByAppendingString:[values objectAtIndex:i]];
But the disadvantage is, that every time a new string is created, which is wasting memory
You should propably use a NSMutableString instead. And then you can just call
[params appendString:[values objectAtIndex:i]];

Vary number of objects in array

In my app the user can create unlimited UITextFields. then I get the information of all of them and upload in a json file:
NSString *object;
NSString *object2;
NSString *object3;
NSString *object4;
NSString *object5;
NSString *size;
for (UITextField *text in array2) {
int touchedtag = text.tag;
NSUInteger tagCount = touchedtag;
switch (tagCount) {
case 1:
object = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 2:
object2 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 3:
object3 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 4:
object4 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 5:
object5 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
default :
break;
}
}
NSArray *keys = [NSArray arrayWithObjects:#"text", #"text2", #"text3", #"text4", #"text5", nil];
NSArray *objects = [NSArray arrayWithObjects:object,object2,object3, object4, object5, nil];
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSString* jsonString = [jsonDictionary JSONRepresentation];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
[jsonData writeToFile:path atomically:YES];
NSString *destDir = #"/sandbox/";
[[self restClient] uploadFile:filename toPath:destDir
withParentRev:nil fromPath:path];
[[self restClient] loadMetadata:#"/sandbox/"];
}
The problem is that the number of objects is not independent (object, object2...). so if the user creates less than 5 text fields the app crashes, while if more than 5 nothing happens, but the info for tag 6 and on is not recorded. How can I vary the number of objects according to the number of fields created?
Learn how to use NSMutableArray: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
It allows you to add and remove objects "on the fly", just like using a Vector in many other languages. (Java, ActionScript, C++...)
Example:
(someValue is a previously declared variable)
stuff = [[NSMutableArray alloc] initWithCapacity: someValue];
[stuff insertObject:object1 atIndex:0];
[stuff insertObject:object2 atIndex:1];
[stuff insertObject:object2 atIndex:2];
...
This code is to initialize it with a specific number of objects, which seeems to be what you are looking for. NSMutableArray also allows you to adjust the number of objects in the array at a later time.

Objective C How to fill rangeOfString of a NSArray?

I wonder if it is possible to fill rangeOfString objects of a NSArray. Because I have a long list of objects for after rangeOfString:
NSArray biglist´s count is higher than list´s count.
I want to filter away the objects from the small list of the main list.
Please tell me if this is not clear.
My codes below:
NSArray *biglist = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"mainlist" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"]];
NSArray *list = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"smalllist" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"]];
for (NSString *listword in list);
NSMutableArray *wordlist = [[NSMutableArray alloc] init];
NSMutableArray *worindex = [[NSMutableArray alloc] init];
NSMutableIndexSet *mindexes = [[NSMutableIndexSet alloc] init];
NSMutableDictionary *mutdic = [[NSMutableDictionary alloc] init];
NSMutableArray *mutarray = [[NSMutableArray alloc] init];
for (NSString *s in mainlist)
{
NSRange ran = [s rangeOfString:listword];
if (ran.location !=NSNotFound)
{
//my codes here
}
}
EDIT:
I think I can solve this by writing
int i;
for (i = 0; i<[list count]; i++)
{
NSString *same = [list objectAtIndex:i];
NSLog (#"listword: %#", same);
}
But I am not sure where to place it, inside the for loop s in mainlist or outside.
EDIT: This for loop works inside the main for loop.
EDIT:
Tried these codes, but it doesnt work somehow..
NSArray *list = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"small" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"]];
NSArray *mainlist = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"mainlist" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"]];
NSMutableArray *large = [NSMutableArray arrayWithArray:mainlist];
NSArray *newlarge;
for (NSString *listword in list)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(SELF beginswith[c] %#)",listword];
newlarge = [large filteredArrayUsingPredicate:predicate];
}
NSLog (#"large: %#", newlarge);
NSLog (#"finished!");
"I want to filter away the objects from the small list of the main list."
If I understand correctly, you want to remove an array of items from another array. You don't want to do that much work and allocations inside an n^2 loop.
This removes an array of items from another array. Depending on how large your array is you may need to optimize further but this works:
NSArray *small = [NSArray arrayWithObjects:#"three", #"two", nil];
NSMutableArray *large = [NSMutableArray arrayWithObjects:#"one", #"two", #"three", #"four", nil];
[large removeObjectsInArray:small];
// print
for (NSString *current in large)
{
NSLog(#"item: %#", current);
}
This outputs:
2011-10-13 08:39:21.176 Craplet[5235:707] item: one
2011-10-13 08:39:21.178 Craplet[5235:707] item: four
I figured it out by myself and solved this :)
It works almost perfectly.
My codes:
NSArray *big = [[NSArray alloc] initWithObjects:#"hello ->mache", #"heisann hoppsann ->hiya", #"nei men ->da", #"however ->what", #"may ->april", #"mai ->maj", nil];
NSArray *small = [[NSArray alloc] initWithObjects: #"heisann ", #"nei men ", #"however ", #"mai", nil];
NSMutableArray *smallwithh = [[NSMutableArray alloc] init];
NSMutableIndexSet *mindexes = [[NSMutableIndexSet alloc] init];
for (NSString *same in small)
{
NSLog (#"listword: %#", same);
for (NSString *s in big)
{
NSRange ran = [s rangeOfString:same];
if (ran.location !=NSNotFound)
{
[smallwithh addObject:s];
NSUInteger ind = [big indexOfObject:s];
[mindexes addIndex:ind];
}
}
}
NSLog (#"smallwith: %#", smallwithh);
[smallwithh release];
NSMutableArray *newWords =[NSMutableArray arrayWithArray: big];
[newWords removeObjectsAtIndexes: mindexes];
[big release];
[small release];
NSLog (#"newWords: %#", newWords);