How do you enumerate through an array in Obj-C? - objective-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.

Related

NSObject not retaining

Process -
NSObject Class used to generate a card with certain properties. This is added to a MutableArray and used accordingly. However, after the function to determine the hand outcome in another class, the MutableArray loses all it's values.
Now I know a MutableArray simply points to the objects as opposed to holding them, so for it to lose all it's values I'm assuming the objects are being swept up by ARC.
-(void)rankHand {
NSString *echo = [Hand returnHandRank:_hand withString:false]; // 7 values in _hand
// 0 values in _hand.
NSLog(#"%#", echo);
}
After breakpointing to see the issue, the issue arises after returnHandRank: withString:
#interface Cards : NSObject
#property (nonatomic, strong) NSString *face;
#property (nonatomic, strong) NSString *suit;
#property (nonatomic, strong) NSString *symbol;
#property (nonatomic) int prime;
#property (nonatomic) int rankByInt;
+(NSMutableArray*)createDeck:(id)sender {
[sender removeAllObjects];
NSArray *faces = [[NSArray alloc] initWithObjects:#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K", nil];
NSArray *suits = [[NSArray alloc] initWithObjects:#"h",#"d",#"c",#"s", nil];
NSArray *primes = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:41],[NSNumber numberWithInt:2],[NSNumber numberWithInt:3],[NSNumber numberWithInt:5],[NSNumber numberWithInt:7],[NSNumber numberWithInt:11],[NSNumber numberWithInt:13],[NSNumber numberWithInt:17],[NSNumber numberWithInt:19],[NSNumber numberWithInt:23],[NSNumber numberWithInt:29],[NSNumber numberWithInt:31],[NSNumber numberWithInt:37], nil];
for (int i = 0; i < 52; i++) {
Cards *card = [[Cards alloc]init];
card.face = [NSString stringWithFormat:#"%#", faces[i % 13]];
card.suit = [NSString stringWithFormat:#"%#", suits[i / 13]];
card.rankByInt = i % 13;
card.symbol = [Cards symbolForSuit:card.suit];
card.prime = [[primes objectAtIndex:(i % 13)] intValue];
[sender addObject:card];
}
[sender shuffle];
return sender;
}
Creates the _deck then _hand is filled by
[_hand addObject:[_deck objectAtIndex:0]];
[_hand addObject:[_deck objectAtIndex:1]];
[_hand addObject:[_deck objectAtIndex:3]];
[_hand addObject:[_deck objectAtIndex:4]];
[_hand addObject:[_deck objectAtIndex:5]];
[_hand addObject:[_deck objectAtIndex:7]];
[_hand addObject:[_deck objectAtIndex:9]];
returnHandRank: withString: is a very long function in the Hand class. So that is why I'm assuming they're not being retained.
Can anyone elaborate? I see it pointless to add the cards again from the _deck, it will it be the best solution?
EDIT: Added returnHandRank: withString:
+(NSString *)returnHandRank:(id)cards withString:(BOOL)returnString {
NSArray *combinations = [self returnCombinations];
cards = [self organizeCardsRankOrder:cards];
__block int maxRank = 0;
__block int maxValue = 0;
for (int i = 0; i < [combinations count]; i++) {
NSArray *splitString = [combinations[i] componentsSeparatedByString:#" "]; // splits the combination string.
NSArray *pointerArray = [[NSArray alloc] initWithObjects:
[NSNumber numberWithInt:[splitString[0] intValue]],
[NSNumber numberWithInt:[splitString[1] intValue]],
[NSNumber numberWithInt:[splitString[2] intValue]],
[NSNumber numberWithInt:[splitString[3] intValue]],
[NSNumber numberWithInt:[splitString[4] intValue]],
nil]; // turns the combinations into int values in an array.
NSMutableArray *fiveCardHand = [[NSMutableArray alloc] initWithObjects:
[cards objectAtIndex:[[pointerArray objectAtIndex:0] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:1] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:2] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:3] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:4] intValue]],
nil]; // Create the 5 card hand for the combination loop we are in, we'll now check this to see what rank it returns.
//Check for hand rank.
fiveCardHand = [self organizeCardsRankOrder:fiveCardHand];
NSArray *fiveCardHandOrganized = fiveCardHand;
int strength = [self handRankWithFiveCards:fiveCardHandOrganized];
if (strength > maxRank) {
maxRank = strength;
maxValue = 0;
}
int value = [self associateStrengthToHand:fiveCardHandOrganized andHand:strength];
if (value > maxValue) {
maxValue = value;
}
}
if (returnString) {
return [self handForStrengthWithStrength:maxRank];
} else {
return [NSString stringWithFormat:#"%i", maxValue];
}
}
There have been a few recent question involving combinations, so unless you are creating accounts we suspect there is homework afoot... No problem, let's see if we can point you in the right direction. However we cannot answer the question, not because it might be homework but because there is not sufficient information to do so.
Now I know a MutableArray simply points to the objects as opposed to holding them,
Correct so far...
so for it to lose all it's values I'm assuming the objects are being swept up by ARC.
but now completely wrong :-( You are misunderstanding how automatic memory management in Objective-C works. First forget "retain", modern ARC-based management is about ownership - whether a variable storing a reference asserts ownership over the object the reference references. When it does assert ownership the variable has the attribute strong, when it stores a reference but does not assert ownership then it has the attribute weak (there are some other ownership attributes you will come across later, they can be ignored for the moment). Object reference variables by default have the attribute strong.
Let's try an analogy:
Consider a balloon ("object"), it will float away unless it is held down; and a hand ("variable"), which holds things.
Many different hands can hold strings (references) attached to the same balloon.
If the hand holds a string tightly (strong) the ballon cannot float away.
If the string is just laying on the palm of the hand (weak) the ballon will float away unless at least one other hand is holding another string attached to the ballon tightly.
A balloon will not float away as long as at least one hand is holding a string tightly.
ARC is the breeze, it blows away balloons not held tightly.
An unannotated variable defaults to strong, so when a reference is stored in it the variable asserts ownership of the referenced object and it will not be cleared away by ARC. An instance variable of a class, or a standard (strong) property, all assert ownership. All the standard collections (arrays, dictionaries, sets) assert ownership over the objects referenced by the references stored in the collection.
Therefore, if you store a reference in an NSMutableArray the referenced object will not be cleared away by ARC as long as the reference remains in the array. If you mutate the array and remove a reference then the object referenced by it will be recycled (returned to the available memory pool) by ARC if and only if there are no other references to it stored in strong variables.
The array itself will stay around as long as a reference to it is stored in a strong variable. When there is no strong reference remaining to the array the array itself will be recycled by ARC, in the process all references stored in the array will be removed and if those references are the last strong ones to the referenced objects they too will be recycled.
Hope that helps and understanding how this works will help you find out where you are either emptying your array, or losing all strong references to the array itself; e.g. by assigning a new reference (or nil) to the variable(s) referencing the array.
Now let's look at some of your code:
NSArray *suits = [[NSArray alloc] initWithObjects:#"h",#"d",#"c",#"s", nil];
This is old style syntax, you can more easily create an NSArray using an array literal, #[ ... ]:
NSArray *suits = #[#"h", #"d", #"c", #"s"];
There are no NSMutableArray literals so you use an NSArray one an make a mutable copy: [#[ ... ] mutableCopy] or the shorter #[ ... ].mutableCopy (opinions differ on the use of the latter). There is also a literal for NSNumber objects, your code:
[NSNumber numberWithInt:41]
can simply be replaced by #41.
Using the above literals will make your code shorter and easier to read.
Now your statement:
card.face = [NSString stringWithFormat:#"%#", faces[i % 13]];
suggests a misunderstanding of how references and immutable objects work. An NSString object is immutable, once created its value will never change. The method stringWithFormat: constructs an NSString according to its format and arguments, which in this case is a single string, so you are just copying the string equivalent to:
card.face = [faces[i % 13] copy];
However a copy of an immutable object is just the original object. You know faces contains only immutable strings as you create it using string literals, so the above is equivalent to:
card.face = faces[i % 13];
Important: You can use a mutable, NSMutableString, reference as an NSString one by sub-classing, so the last step here dropping the copy is only valid if you know the reference is to an NSString object and not to an NSMutableString one.
Having used direct indexing on faces and suits you switch to long form:
card.prime = [[primes objectAtIndex:(i % 13)] intValue];
and in a few other places. All of them can be replaced by [...], e.g.:
card.prime = [[primes[i % 13] intValue];
While you uses of division (i / 13) and remainder (i % 13) are all correct you might want to consider using two nested loops to avoid them, e.g. something like:
for(int suitRank = 0; suitRank < 4; suitRank++)
{ for(int cardRank = 0; cardRank < 13; cardRank++)
{ // now use suitRank for i / 13 and cardRank for i % 13
The above is all just tidying up to make your code shorter, more readable, and less error prone. Now a more serious issue:
+(NSMutableArray*)createDeck:(id)sender {
[sender removeAllObjects];
Never do this! While id has it uses it reduces the compilers ability to check your code is correct and can result in your code going wrong when it is run for simple errors the compiler would have caught. Here sender is clearly meant to be a reference to a mutable array, declare it as such:
+ (NSMutableArray *)createDeck:(NSMutableArray *)sender
{
[sender removeAllObjects];
Later (after applying the above use of literals) you have:
NSMutableArray *fiveCardHand = #[ cards[[pointerArray[0] intValue]],
...
].mutableCopy;
//Check for hand rank.
fiveCardHand = [self organizeCardsRankOrder:fiveCardHand];
Here you:
create a mutable array
assign a reference to it to fiveCardHand
overwrite the reference in fiveCardHand with the result of organizeCardsRankOrder:
So here you appear not to have mutated the array referenced by fiveCardHand but instead changed the variable to reference a different array. You don't need to use mutable arrays to do that, you are mutating the variable holding the reference not the referenced array. Now "appear" was used here as you have not supplied the code of organizeCardsRankOrder:, maybe that method does mutate the array passed to it, if that is the case it does not need to also return it and there is no need for the assignment to the variable. So look at your code carefully here and decide whether you are mutating arrays or just variables and change it accordingly.
Finally you do not provide any declarations in the question for _deck and _hand. By naming convention you might be directly accessing the backing variable of a property (doing this is often best avoided), or accessing an instance variable, both of some unspecified class. Therefore we cannot provide any real help with these, just check that if they are connected to an instance that you are using the same instance everywhere you expect to - a common early error is to set an instance variable in one instance, try to read it from another instance, and then wonder why the value is different...
HTH, happy debugging!

removing null from arrays in Object-c

I have this snipped of code that results in an array with a whole bunch of "<null>" throughout and I need to figure out how to remove them. Obviously after smashing my head against the keyboard I'm asking for some help.
In my .h I have declared:
NSArray *sortedContacts;
NSArray *rawContacts;
And then in .m:
-(void) buildContacts {
ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
NSArray *contacts = [addressBook people];
rawContacts=contacts;
NSArray *firstNames = [rawContacts valueForKey:#"First"];
NSArray *lastNames = [rawContacts valueForKey:#"Last"];
NSArray *organization = [rawContacts valueForKey:#"Organization"];
NSMutableArray *fullNames = [NSMutableArray array];
for(int i = 0; i < [firstNames count]; i++)
{
NSString *fullName = [NSString stringWithFormat:#"%# %# %#",
[firstNames objectAtIndex:i],
[lastNames objectAtIndex:i],
[organization objectAtIndex:i]];
[fullNames addObject:fullName];
}
NSMutableArray *fullList = [[NSMutableArray alloc]initWithArray:fullNames];
[fullList removeObjectIdenticalTo: #"<null>"];
sortedContacts = [fullList sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#",sortedContacts);
}
I've tried so many things that I just can't see the forest for the trees anymore.
The text <null> is how the singleton instance of NSNull describes itself. That is, it's what -[NSNull description] returns.
In turn, these NSNull objects are getting into your firstNames, lastNames, and organization arrays because that's what Key-Value Coding does when you call -valueForKey: on an array and some of the elements return nil when that message is forwarded on to them with the same key. That is, calling [rawContacts valueForKey:#"First"] causes NSArray to call [element valueForKey:#"First"] for each element in rawContacts and to put the result in the array it builds. But, since an array can't contain nil, if one of those elements returns nil from [element valueForKey:#"First"], an NSNull object is added in its place.
Then, you are formatting the string fullName from the corresponding elements of firstNames, lastNames, and organization. You need to check if any of those elements are NSNull using if ([value isKindOfClass:[NSNull class]]) and handling that. For instance, you might just skip that record. Or you might combine the available fields and leave out any unavailable ones.
In any case, none of the elements of fullList will be #"<null>" because formatting values into #"%# %# %#" can never result in that string. (It might be #"<null> <null> <null>" or something like that, but never just #"<null>".)
A quick look at your code suggests you cannot get any empty strings added to your array, (a) you add elements using:
[fullNames addObject:fullName];
and fullName is created using:
[NSString stringWithFormat:#"%# %# %#" ...
so even if the %#'s get replaced by nothing you'll still have 2 spaces...
Maybe this is why all the things you've tried fail, if you're looking for empty strings you won't find them.
(Addendum: Question now says you're looking for #"<null>", you won't get that either for the same reason - there is at least two spaces in your string.)
The simple answer to removing invalid entries in fullNames is not to add them in the first place. You are adding elements in a loop (for), and conditional logic (e.g. if) inside the loop to determine whether you have something valid to add - however you define "something valid" - and only add an item to fullNames if so.
HTH
I'm not really familiar with the AddressBook framework, however this might be what's causing the confusion:
The values you collect in your arrays firstNames, lastNames and organization can be of type NSString or NSNull. You have to do any null-checking within the for-loop, before the fullName-string is constructed.
Remove this useless line:
[fullList removeObjectIdenticalTo: #"<null>"];
And replace the contents of your for-loop with the following code:
for(int i = 0; i < [firstNames count]; i++)
{
NSString *firstName = [firstNames objectAtIndex:i];
NSString *lastName = [lastNames objectAtIndex:i];
NSString *org = [organization objectAtIndex:i];
NSMutableArray *namesArray = [NSMutableArray array];
if ([firstName isKindOfClass:[NSString class]])
[namesArray addObject:firstName];
if ([lastName isKindOfClass:[NSString class]])
[namesArray addObject:lastName];
if ([org isKindOfClass:[NSString class]])
[namesArray addObject:org];
if (namesArray.count > 0)
[fullNames addObject:[namesArray componentsJoinedByString:#" "]];
}

-[__NSCFNumber addObject:]: unrecognized selector sent to instance [duplicate]

This question already has answers here:
How can I debug 'unrecognized selector sent to instance' error
(9 answers)
Closed 8 years ago.
I created a multidimensional array as follows:
NSMutableArray *subArray = [NSMutableArray arrayWithObjects:[NSString string], [NSNumber numberWithInt:0], [NSMutableArray array], nil];
self.dataArray = [[NSMutableArray alloc] initWithCapacity:9];
for (int i = 0; i < 9; i++) {
[self.dataArray addObject:subArray];
}
then when I try to access and change values like this
NSNumber *num = self.dataArray[0][1];
int numInt = [num intValue];
NSNumber *newNum = [NSNumber numberWithInt:numInt + 1];
[self.dataArray[0][1] addObject:newNum];
// add item to dataArray
NSMutableArray *tmpArr= self.dataArray[0][2];
[tmpArr addObject:item];
[self.dataArray[0][2] addObject:tmpArr];
but I'm getting
-[__NSCFNumber addObject:]: unrecognized selector sent to instance
what exactly is the problem, I don't understand, thanks in advance!
In the first line you are treating the object in the array as a NSNumber (which it obviously is):
NSNumber *num = self.dataArray[0][1];
And here you treat the exact same object like an NSMutableArray:
[self.dataArray[0][1] addObject:newNum];
That won't work, because that object is an instance of NSNumber.
I don't know what you achieve so I can't help you with the correct code, but that's where your problem is. Maybe you just wanted to write:
[self.dataArray[0][2] addObject:newNum];
You should probably stop to use an "inner array" as data storage and switch to using a proper subclass. Currently your code is pretty much unreadable, using proper Objects to store your values would improve it a lot.
Btw, your multidimensional array is actually just one dimensional, because you add the exact same array multiple times.
You probably want to do this:
for (int i = 0; i < 9; i++) {
NSMutableArray *subArray = [NSMutableArray arrayWithObjects:[NSString string], [NSNumber numberWithInt:0], [NSMutableArray array], nil];
[self.dataArray addObject:subArray];
}
NSNumber *num = self.dataArray[0][1];
int numInt = [num intValue];
NSNumber *newNum = [NSNumber numberWithInt:numInt + 1];
[self.dataArray[0][1] addObject:newNum]; // 3. also, possibly here
// add item to dataArray
NSMutableArray *tmpArr= self.dataArray[0][2]; // 1. here
[tmpArr addObject:item]; // 2. and here
[self.dataArray[0][2] addObject:tmpArr];
You're getting this error because the first line I marked, tmpArr is actually of type NSNumber. NSNumber is a class cluster, which is why you're seeing __NSCFNumber throw the error. All that is, is just a private subclass of NSNumber.
So the error is being thrown because you're trying to call addObject on a type of object that doesn't support it. Personally I wouldn't store more than one type of object in an array, but I don't know exactly what you're doing. Assuming you don't change the way you're storing things, what you can do is this:
NSMutableArray *tmpArr= self.dataArray[0][2];
if ([tmpArr isKindOfClass:[NSMutableArray class])
{
[tmpArr addObject:item];
}
else
{
NSLog(#"Woops, trying to add an object to something that's not a mutable array");
}
You would have to do this everytime you try to store an object into an array that you're pulling out of self.dataArray. What this does is verify that tmpArr is what you think it is.
Alternatively, you could check if it responds to addObject
if ([tmpArr respondsToSelector:#selector(addObject:)])
{
[tmpArr addObject:item];
}
The second way doesn't care what class it is, only if the method addObject can be used.

reassigning a bool value to NSArray

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.

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.