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

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.

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:#" "]];
}

Returning an array (Warning: Function returns address of local variable)?

Returning an array (Warning: Function returns address of local variable) ?
interface
int* decimalConversion(int iX);
implementation
int* decimalConversion(int iX){
int iMult[10] = {0,0,0,0,0,0,0};
...
return iMult; // <-- Warning: Function returns address of local variable
}
You should allocate space for the array, you're returning the address of an array that was created on the stack (hence local variable warning) if you're using C in that function use malloc(my_arr_size) if not use obj-c's alloc.
Example:
int *my_arr = calloc(10, sizeof(int)); //make sure we get zeroed memory
//fill array
return my_arr; //should no longer give a warning
When done with it just use free(my_arr) same as release. Reason I did this in C is because I can see that you're returning an int* type and using C style declarations so if you're doing it in Obj-c let me know and I can change my answer's example.
The reason you are getting this error is because local arrays get put on the stack, when you return that array you return an address in a stack frame. The problem is that when that method finishes execution that stack frame is no longer valid and therefore you cannot expect any data that was on that frame to be valid (although there are cases when this does work but it is considered bad practice). By allocating that array on the heap you can return a heap address where your data is assured to exist until you call free() on the pointer to that data.
If you are doing this for an app written in Objective-C, I would suggest using NSArray. NSArray is an Objective-C class for immutable arrays, and doesn't require that you manually allocate memory. The only turnoff is that you have to encapsulate your integers in NSNumber objects. An example would be:
NSArray * getNums (int num) {
NSArray * result = [NSArray arrayWithObjects:[NSNumber numberWithInt:num-1], [NSNumber numberWithInt:num], [NSNumber numberWithInt:num+1], nil];
return result;
}
...
NSArray * myList = getNums(10);
NSLog(#"First: %d", [[myList objectAtIndex:0] intValue]);
NSLog(#"Second: %d", [[myList objectAtIndex:1] intValue]);
NSLog(#"Third: %d", [[myList objectAtIndex:2] intValue]);
You can alternatively do this:
NSArray * getNums (int num) {
NSMutableArray * array = [NSMutableArray array];
[array addObject:[NSNumber numberWithInt:num-1]];
[array addObject:[NSNumber numberWithInt:num]];
[array addObject:[NSNumber numberWithInt:num+1]];
return array;
}
...
NSArray * myList = getNums(10);
for (int i = 0; i < [myList count]; i++) {
NSLog(#"myList[%d] = %d", i, [myList objectAtIndex:i]);
}
The only difference is that NSMutableArray allows you to add/remove elements after the fact.

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.

Add a (double) variable to NSMutableArray

I have a user input variable and i want to add that to an array. The firstStore is a BOOL type t determine if the array has been initialize. So the first time the STORE was called, it would initialize the array. I tried to make num equal to the operand (which is a double) by masking it NSNumber but that doesn't seem to work because i have this error "NSNumber may not respond to +operand", also the program crashes when it hits the line [memArray addObject:num]. I'm new at this stuff so any help would be greatly appreciated.
else if ([operation isEqual:#"Store"]) {
if(!firstStore){
memArray = [[NSMutableArray alloc] init];
NSNumber *num = [NSNumber operand];
[memArray addObject:num];
firstStore = YES;
} else {
//NSNumber *num = [NSNumber operand];
//[memArray addObject:num];
}
}
Try changing [NSNumber operand] to [NSNumber numberWithDouble:operand].
"NSNumber may not respond to +operand" means what is says: There is no such class-method operand. What was it supposed to do?
You dont need a bool to track if memArray is initialized. use:
if(!memArray)
or
if (memArray == nil)