reassigning a bool value to NSArray - objective-c

i have an NSArray with bool values:
NSArray* boolResults = [super foo:values];
how can i change the value in cell 0?
i tried the following:
boolResults[0] = #NO;
this results in an error: Expected method to write array element not found on object of type 'NSArray *'
and also this:
BOOL* b = &[[array objectAtIndex:i] boolValue];
got the following error: Address expression must be an lvalue or a function designator
i don't wish to convert this NSArray to NSMutableArray in order to set this value, is there a normal way to do this?
Thanks

If the array isn't mutable, then you can't change that value. The solutions are two:
Make the array mutable;
Let the array contain mutable objects.
Since you don't want to use a mutable array, I'll make you an example with the second solution. Since there isn't a mutable number in the standard framework, I'll wrap it into NSMutableData. The example supposes that you have an array with a single object, with value #YES, and you want to change it to #NO:
NSNumber* number= #YES;
NSMutableData* data=[[NSMutableData alloc]initWithData: [NSKeyedArchiver archivedDataWithRootObject: number]];
NSArray* array= #[data]; // Now you have an array with a single value
// You want to change the first value to #NO:
number= #NO;
[array[0] setData: [NSKeyedArchiver archivedDataWithRootObject: number]];

No. NSArrays are immutable. You could reassign your pointer to the array with a modified NSArray.
NSArray *anArray = [super foo:values]
NSMutableArray *mutableCopy = [anArray mutableCopy];
// change your mutable copy and then reassign
anArray = [mutableCopy copy];
And just like NSArray, NSNumbers are also immutable, so something like [anArray[0] setBoolValue:NO] does not exist.

Related

NSSet: return an NSArray of objects sorted by a list of strings on certain property of each object?

I have an NSSet of objects.
Each object in the set is an instance of MyObject with a property called name.
I have another NSArray called nameIndexes which contains name values.
I would like to have a function that takes the NSSet and returns a sorted array sorted using the name property based on its position in the nameIndexes array.
Edit:
Sorry for my misleading, here is an example:
I have a set of MyObject(may not be in the same order):
MyObject1 {name:#"A"}
MyObject2 {name:#"B"}
MyObject3 {name:#"C"}
I have another array of names:
{"B", "A", "C"}
I want an NSArray of:
{MyObject2, MyObject1, MyObject3}
NSSet *set = //your set.
NSArray *nameIndexes = //the array of names for sorting.
NSArray *result = [[set allObjects] sortedArrayUsingComparator:^NSComparisonResult(MyObject *obj1, MyObject *obj2) {
int index1 = [nameIndexes indexOfObject:obj1.name];
int index2 = [nameIndexes indexOfObject:obj2.name];
return [[NSNumber numberWithInt:index1] compare:[NSNumber numberWithInt:index2]];
}];
Not 100% certain what you are asking but this will sort take the set of names and turn it into a sorted array sorted based on the index of the names in a second array.
After edit
Edit... ok, so your edit completely changed the question. Anyway, from the edit you can just do...
NSArray *result = [[set allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:"name" ascending:YES]]];
This will take the set and return a sorted array sorted by the name property of the objects in alphabetical order.

Create Instance variables at runtime

I want to create instance variables dynamically at runtime, and I want to add these variables to a category. The number of the instance variables may change based on the configuration/properties file which I am using for defining them.
Any ideas??
Use Associative References - this is tricky, but that is the mechanism invented specifically for your use case.
Here is an example from the link above: first, you define a reference and add it to your object using objc_setAssociatedObject; then you can retrieve the value back by calling objc_getAssociatedObject.
static char overviewKey;
NSArray *array = [[NSArray alloc] initWithObjects:# "One", #"Two", #"Three", nil];
NSString *overview = [[NSString alloc] initWithFormat:#"%#", #"First three numbers"];
objc_setAssociatedObject (
array,
&overviewKey,
overview,
OBJC_ASSOCIATION_RETAIN
);
[overview release];
NSString *associatedObject = (NSString *) objc_getAssociatedObject (array, &overviewKey);
NSLog(#"associatedObject: %#", associatedObject);
objc_setAssociatedObject (
array,
&overviewKey,
nil,
OBJC_ASSOCIATION_ASSIGN
);
[array release];
I'd be inclined to just use a NSMutableDictionary (see NSMutableDictionary Class Reference). Thus, you would have an ivar:
NSMutableDictionary *dictionary;
You'd then initialize it:
dictionary = [NSMutableDictionary dictionary];
You can then save values to it dynamically in code, e.g.:
dictionary[#"name"] = #"Rob";
dictionary[#"age"] = #29;
// etc.
Or, if you are reading from a file and don't know what the names of the keys are going to be, you can do this programmatically, e.g.:
NSString *key = ... // your app will read the name of the field from the text file
id value = ... // your app will read the value of the field from the text file
dictionary[key] = value; // this saves that value for that key in the dictionary
And if you're using an older version of Xcode (before 4.5), the syntax is:
[dictionary setObject:value forKey:key];
Depends on exactly what you want to do, the question is vague but if you want to have several objects or several integers or so on, arrays are the way to go. Say you have a plist with a list of 100 numbers. You can do something sort of like this:
NSArray * array = [NSArray arrayWithContentsOfFile:filePath];
// filePath is the path to the plist file with all of the numbers stored in it as an array
That will give you an array of NSNumbers, you can then turn that into an array of just ints if you want like this;
int intArray [[array count]];
for (int i = 0; i < [array count]; i++) {
intArray[i] = [((NSNumber *)[array objectAtIndex:i]) intValue];
}
Whenever you want to get an integer from a certain position, lets say you want to look at the 5th integer, you would do this:
int myNewInt = intArray[4];
// intArray[0] is the first position so [4] would be the fifth
Just look into using a plist for pulling data, it will them be really easy to create arrays of custom objects or variables in your code by parsing the plist.

How does one populate an NSMutable array of NSMutableSets?

I am using this code in a loop to populate an NSMutable Array of NSMutableSets (of NSString objects). The index of the NSSet is based on the length of the word.
// if set of this length not initialized yet, initialize set.
wordIndex = [NSString stringWithFormat:#"%d", currentWordLength];
if ([myWordArray objectForKey:wordIndex] == nil)
[myWordArray setObject:[[NSMutableSet alloc] initWithObjects:currentWord, nil] forKey:wordIndex];
else
[[myWordArray objectForKey:wordIndex] addObject:currentWord];
The final intention is to split up an array of words into an array of sets of words grouped by their lengths.
However, I see that [myWordArray count] is 0 after this. Why?
You are confusing the methods of NSMutableDictionary and NSMutableArray: In Objective-C arrays do not have keys but have indexes. If you change the class for myWordArray to NSMutableDicitionary it should work.
Try this, it looks very much like your logic, but (1) it uses NSNumbers as keys, which makes a little more sense, (2) handles the missing set condition more simply, but just adding the set, and (3) breaks up the source lines somewhat for easier debugging...
NSArray *inputStrings = // however these are initialized goes here
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *currentString in inputStrings) {
NSInteger currentWordLength = currentString.length;
wordIndex = [NSNumber numberWithInt:currentWordLength];
NSMutableSet *wordSet = [result objectForKey:wordIndex];
if (!wordSet) {
wordSet = [NSMutableSet set];
[result setObject:wordSet forKey:wordIndex];
}
[wordSet addObject:currentWord];
}
If you still have an empty dictionary after running this, it might be simpler to watch what's happening by stepping through it.

Why does fast enumeration not skip the NSNumbers when I specify NSStrings?

I thought that I knew how to use fast enumeration, but there is something I don't understand about it. If I create three NSString objects and three NSNumber objects and put them in an NSMutableArray:
NSString *str1 = #"str1";
NSString *str2 = #"str2";
NSString *str3 = #"str3";
NSNumber *nb1 = [NSNumber numberWithInt:1];
NSNumber *nb2 = [NSNumber numberWithInt:2];
NSNumber *nb3 = [NSNumber numberWithInt:3];
NSArray *array = [[NSArray alloc] initWithObjects:str1, str2, str3, nb1, nb2, nb3, nil];
then I make do fast enumeration on all NSString objects, like this:
for (NSString *str in array) {
NSLog(#"str : %#", str);
}
In the console, I get this result :
2011-08-02 13:53:12.873 FastEnumeration[14172:b603] str : str1
2011-08-02 13:53:12.874 FastEnumeration[14172:b603] str : str2
2011-08-02 13:53:12.875 FastEnumeration[14172:b603] str : str3
2011-08-02 13:53:12.875 FastEnumeration[14172:b603] str : 1
2011-08-02 13:53:12.876 FastEnumeration[14172:b603] str : 2
2011-08-02 13:53:12.876 FastEnumeration[14172:b603] str : 3
I logged only the NSStrings, but I get a line for every object in the array, even the NSNumbers and I don't understand why. Does fast enumeration always use every object contained in an array?
When you write a forin loop like that, it casts every object in the array as an NSString, then prints them out as requested.
If you want only the NSStrings, you would need to write something like this:
for (id obj in array) {
if ([obj isKindOfClass:[NSString class]]) {
NSLog(#"str: %#", obj);
}
}
The for all loop doesn't know the difference between NSStrings and Integers -- it will simply go through the entire array, cast each as an NSString, and print them out as you asked.
I'm pretty sure that fast enumeration returns all objects in the array- all that you're doing in for (NSString *str in array) is typecasting str to an NSString. In the body of the loop you need to check the class of the returned object to make sure that it is an NSString.
for(NSString *str in array)
{
if([str isKindOfClass:[NSString class]])
NSLog(#"str : %#", str);
}
Objective-C is dynamically typed, meaning that at runtime (when the loop actually runs), objects are all effectively one type (id) with different classes. The language allows optional compile-time static typing, but all that does is check whether the messages you're sending are valid for the type you've marked. It doesn't actually change the behavior of your program. If you cast an object to be a different type than it actually is, all you're doing is lying to the compiler and defeating its type-checker.
Every object that descends from NSObject implements the method - (NSString)description, %# in Objective-C formate string will take the corresponding argument for the %# and call its description method, Most subclasses of NSObject will implement there own version of - (NSString)description. The same thing happens when you type
> po anObject
in the debugger.
for (NSString *str in array) {
is a way to enumerate through all the elements in array.
You expectative that by specifying NSString you get only the objects of that type is not correct. Rather, all the objects pointers are cast to that type (NSString*).
Have a look at Fast Enumeration in The Objective-C Programming Language guide.
I don't understand where is the unexpected behavior, using the enhanced for loop in an NSMutableArray will just iterate thru every single object in the array which in your case is 6, the result is correct and expected.
The numbers will just get casted to Strings.
in fast enumeration no typecasting,just assigning the pointer into new object

How do you enumerate through an array in Obj-C?

I have an array which has several objects (all of different classes) in it. But using enumeration doesn't work on it for some reason.
NSString *arrayString;
NSURL *arrayUrl;
NSProcessInfo *arrayPr;
NSDictionary *arrayDictionary;
NSMutableString *arrayMString;
NSMutableArray *objectArray = [NSMutableArray arrayWithObjects:arrayString,arrayUrl,arrayPr,arrayDictionary,arrayMString,nil];
for( NSString *item in objectArray ){
NSLog(#"Class name is: %#", [item className]);
}
I think it might be something to do with how the objects are been added to the array but i'm new to objective-c and not sure.
you aren't actually populating the array.
NSString *arrayString;
declares a variable, arrayString, of type NSString. it's not initialized (so it deserves to crash when you use the variable -- but may be 0 with some build settings).
so, to assign a variable:
NSString *arrayString = [NSString stringWithFormat:#"sksjdhf %f\n", 3.3];
arrayWithObjects adds objects from the (va list) argument until nil/null/0 is encountered.
you must set up the remainder of your variables/arguments correctly before using them.
this should work as you expect it to:
NSString * str = #"a string";
NSMutableArray *objectArray = [NSMutableArray arrayWithObjects:str, nil];
for (NSObject * item in objectArray) {
NSLog(#"Class name is: %#", [item className]);
}
In the for loop, use an id data type. The id data type is a general purpose data type that can be used to store a reference to any object.
For example:
for ( id item in objectArray ) {
NSLog(#"Class name is: %#", [item className]);
}
Yep, that's how you do it. If you're having trouble, it is not in the enumeration syntax itself.