Errors in trying to cast to NSInteger * and NSMutableArray * - objective-c

I'm a newbie in obj c. So I have a simple question.
I have a matrix of NSInteger values. It is called "curBoard". I want to update value at (x,y) coordinates with value "curStep". I have an arror "operand of type void where arithmetic..."
What am I doing wrong ?
[curBoard replaceObjectAtIndex:x withObject:(NSMutableArray *)[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:(NSInteger *)[NSNumber numberWithInt:curStep]]];
Update:
NSMutableArray *board;
board = [NSMutableArray new];
for(NSInteger i = 0; i<boardSize; i++) {
NSMutableArray *row = [NSMutableArray new];
for(NSInteger j = 0; j < boardSize; j++)
[row addObject:(NSInteger *)[NSNumber numberWithInt:0]];
[board addObject:row];
}

This withObject:(NSInteger *)[NSNumber numberWithInt:curStep]] part is what causing an issue. If you are storing as NSNumber objects, you should just use:
... withObject:[NSNumber numberWithInt:curStep]]
Edit:
From the code posted above, you should add it as:
[row addObject:[NSNumber numberWithInt:0]];
NSInteger is not of pointer type and you should use NSNumber itself to add to array.

Objective-C is basically just a bunch of object syntax strapped to C. The overall effect is something like strapping a jetpack to a horse: sometimes the two parts don't really work together very well. In this case, you're trying to go faster by telling the horse to giddy up, when you should really be opening up the throttle.
NSMutableArray is part of the jetpack—it's an Objective-C object and is only equipped to handle arrays of Objective-C objects. But NSInteger is part of the horse—it's a primitive C integer type, not a real object.*
I know NSInteger is capitalized like a class and has an NS prefix like a class, but it's really a creature of C. You can confirm this yourself—type Cmd-O in Xcode and type "NSInteger" into the Open Quickly dialog that pops up, and you'll be able to jump to its definition. In my current Mac project, that's typedef long NSInteger;; long is one of the primitive C types.
NSNumber exists to bridge the two. It's an object specifically designed to hold the C numeric types inside it. Since NSNumber is an object, NSMutableArray and other Objective-C things can deal with it.
But you can't just cast between NSNumber and NSInteger. NSNumber holds an NSInteger inside it, but that doesn't mean it's actually an NSInteger itself. If you put a sandwich in a plastic bag, you can't eat the bag.
Instead, you have to use NSNumber's +numberWithInteger: method to construct an NSNumber, and -integerValue to get the integer back out of it. (+numberWithInt: and -intValue will usually work, but they may behave differently with very large values, depending on whether your app is running on a 32-bit or 64-bit processor.) Actually, nowadays you can say [NSNumber numberWithInteger:foo] as #(foo) instead, which is a lot shorter.**
So when you add a number, you should be saying:
[row addObject:#(0)];
And when you later want that number back, you'll want to say something like:
n = [[row objectAtIndex:y] integerValue];
The -replaceObjectAtIndex:withObject: error is a different story. -replaceObjectAtIndex:withObject: doesn't return anything at all, so you can't use it as an argument. Luckily, you don't need to in this case. -replaceObjectAtIndex:withObject: doesn't create a new array; it alters the array that's already inside [curBoard objectAtIndex:x], so you don't need to do anything to curBoard. Instead, you can just write:
[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:#(curStep)];
* You actually used NSInteger *, which is slightly different. The * means "pointer to", so NSInteger * is a pointer to a primitive integer. This is sort of like NSNumber *, a pointer to an NSNumber object, so the compiler allows you to cast it.
Note that casting a pointer doesn't convert the data at the other end of the pointer; it just makes the compiler interpret the same data in a different way. If you actually tried to use the NSInteger * pointer to get data, you would either get garbage data or (for reasons too large to fit within this margin) crash.
In this case, though, once you've Jedi mind-tricked the compiler into thinking that value is a pointer to an NSInteger, you try to pass it to to -addObject:. -addObject: expects a pointer to an object, so the compiler balks at passing a pointer to an NSInteger instead.
** This syntax will work as long as you're using the iOS 6 SDK Xcode 4.4 or later, even if you actually run the app on an older iOS. It will also automatically use the right +numberWithWhatever: method for you, so you don't have to worry about picking the best one. When you're using a numeric literal like 0, the parentheses are optional, but they're required when you use a variable or constant. Of course, you can still do it the wordy way if you want, but there's little point nowadays.

Related

Objective-C NSNumber numberWithLongLong creates integer

When I attempt to create an NSNumber using the numberWithLongLong with a number greater than -2 and less than 13 it returns a number that is casted as an (int).
I see this if I look at the Xcode debugger after stepping over my line.
NSNumber* numberA = [NSNumber numberWithLongLong:-2]; //Debugger shows as (long)-2
NSNumber* numberB = [NSNumber numberWithLongLong:-1]; //Debugger shows as (int)-1
NSNumber* numberC = [NSNumber numberWithLongLong:12]; //Debugger shows as (int)12
NSNumber* numberD = [NSNumber numberWithLongLong:13]; //Debugger shows as (long)13
To put my problem in context, I am using a long long value for an epoch date that I will end up serializing using BSON and sending across the wire to a webservice. The webservice requires the date to be a java Long.
Thanks in advance
You have discovered that NSNumber (actually, its CFNumber counterpart) has a cache for integers between -1 and 12 inclusive. Take a look at the CFNumberCreate function in CFNumber.c to see how it works.
It looks like you can force it not to use the cache by passing your own allocator to CFNumberCreate. You'll need to look at the CFAllocator documentation.
But note that the CFNumberCreate manual says this:
The theType parameter is not necessarily preserved when creating a new CFNumber object.
So even if you bypass the cache, you might not get back an object whose objCType is q (which means long long). It looks like the current implementation will return q but that could change in a future version.
You are allowed to write your own NSNumber subclass if you need to guarantee that objCType returns q. Read “Subclassing Notes” in the NSNumber Class Reference.
You can use your webservice without concern.
NSNumber wraps a numeric value (of primitive type) as an object. How NSNumber stores that value is not really your concern (but there is a method to find it out), it is an opaque type. However NSNumber does maintain an internal record of the type used to create it so its compare: method can follow C rules for comparison between values of different types precisely.
For integral types the integral value you get back will be exactly the same, in the mathematical sense, as the one you created the NSNumber with. You can create an NSNumber with a short and read its value back as a long long, and the mathematical value will be the same even though the representation is different.
So you can store your integral date value as an NSNumber and when you read it back as a long long you will get the right value. No need to be concerned how NSNumber represents it internally, and indeed that could potentially change in the future.
(At least one implementation of NSNumber can store values as 128-bit integers, which helps ensure correct semantics for signed and unsigned integers. Also I stressed integral types as with the vagaries of real numbers talking about mathematical exactness is somewhat moot.)
Wait. I think I know what your asking. Try it this way:
NSNumber* numberA = [NSNumber numberWithLongLong:-2LL];
NSNumber* numberB = [NSNumber numberWithLongLong:-1LL];
NSNumber* numberC = [NSNumber numberWithLongLong:12LL];
NSNumber* numberD = [NSNumber numberWithLongLong:13LL];
BTW: it won't matter what the type of the constant is, it will be coerced into a long long when passed to [NSNumber numberWithLongLong:]
UPDATE
Based on #robmayoff's answer, I don't think NSNumber is reliable for your. How are you packing your BSON? is there a way to use NSValue instead of NSNumber?

Using malloc to allocate an array of NSStrings?

Since NSSstring is not defined in length like integer or double, do I run the risk of problems allocating an array of NSStrings for it using malloc?
thanks
ie:
NSString ***nssName;
nssName = (NSString***) malloc(iN * sizeof(NSString*));
the end result with for_loops for the rows is a 2D array, so it is a little easier to work then NSArray(less code).
No problems should arise, allocating an array of NSStrings is like making an array of the pointers to string objects. Pointers are a constant length. I would recommend just using NSArray but it is still fine to use a C array of NSStrings. Note that this may have changed with ARC.
Here is completely acceptable code demonstarting this:
NSString** array = malloc(sizeof(NSString*) * 10); // Array of 10 strings
array[0] = #"Hello World"; // Put on at index 0
NSLog(#"%#", array[0]); // Log string at index 0
Since NSString is an object (and to be more precise: an object cluster) you cannot know its final size in memory, only Objective-C does. So you need to use the Objective-C allocation methods (like [[NSString alloc] init]), you cannot use malloc.
The problem is further that NSString is an object cluster which means you do not get an instance of NSString but a subclass (that you might not even know and should not care about). For example, very often the real class is NSCFString but once you call some of the methods that treat the string like a path you get an instance of NSPathStore2 or whatever). Think of the NSString init methods as being factories (as in Factory Pattern).
After question edit:
What you really want is:
NSString **nssName;
nssName = (NSString**) malloc(iN * sizeof(NSString*));
And then something like:
nssName[0] = #"My string";
nssName[1] = [[NSString alloc] init];
...
This is perfectly fine since you have an array of pointers and the size of pointer is of course known.
But beware of memory management: first, you should make sure the array is filled with NULLs, e.g. with bzero or using calloc:
bzero(nssName, iN * sizeof(NSString*));
Then, before you free the array you need to release each string in the array (and make sure you do not store autoreleased strings; you will need to retain them first).
All in all, you have a lot more pitfalls here. You can go this route but using an NSArray will be easier to handle.
NSStrings can only be dealt with through pointers, so you'd just be making an array of pointers to NSString. Pointers have a defined length, so it's quite possible. However, an NSArray is usually the better option.
You should alloc/init... the NSString*s or use the class's factory methods. If you need an array of them, try NSArray*.
You should not use malloc to allocate data for Objective-C types. Doing this will allocate memory space but not much else. Most importantly the object will not be initialized, and almost as importantly the retain count for the object will not be set. This is just asking for problems. Is there any reason you do not want to use alloc and init?

How to convert an pointer to become a object?

I using something like this : [tmpArray insertObject:something[i] atIndex:i];
But I got a MSG : passing argument 1 of "insertObject:atIndex:" makes pointer from integer without a cast.
What should I do to fix it?
I guess your something[] array is a C array of int, if this is the case you cannot pass its value at that position into an NSArray since it contains objects (and objects in obj-C are referenced by pointers).
What you could do is wrap the int into a NSNumber this way:
[tmpArray insertObject:[NSNumber numberWithInt:something[i]] atIndex:i];
You can't magically make random pointer into an object, you need to understand what the type of the pointer is and do something semantically reasonable with that type. Also, that is not what the error you are seeing is about. The error is because the type expected is a pointer (and an object in ObjC is also a pointer type), and the type info of the thing you are passing is a scalar.
Taking a wild guess, something is probably an array of ints, if so what you want to do is wrap is the integer in an NSNumber in order to store it in a collection:
//assuming this:
int something[100];
//then your line should be this:
[tmpArray insertObject:[NSNumber numberWithInt:something[i]] atIndex:i];
Obviously you will need to unbox the value anywhere you access it. If it is not an array of integers then you will need to do something else (though probably similiar), but without more info I can't tell you exactly what.

objective-c question regarding NSString NSInteger and method calls

I like to create an instance of a custom class by passing it a value that upon init will help set it up. I've overridden init and actually changed it to:
- (id)initWithValue:(NSInteger *)initialValue;
i'd like to store this value in a property of the instantiated object and use the value to generate the filename of a UIImage that will be associated with it.
My question is how best to go about this in Cocoa/Obj-C. is an NSInteger the best parameter for my needs or would an NSString be better? Or is there another design pattern? I'm not familiar with all the creative methods available to help in this situation. Assume my class properties are:
#interface MyView : UIView {
UIImage *image;
NSInteger value;
If I stick with the NSInteger as a parameter I can easily assign value to it. but then would need to generate "image12.png" from that value (assume NSInteger = 12) to set up my UIImage. Something like "image"+initialValue+".png". Which I believe is verbosely expressed in Obj-C as:
NSString *myValue = #"image";
[myValue stringByAppendingString:[initialValue stringValue]]; //NSInteger may not respond to stringValue?!?
[myValue stringByAppendingString:#".png"]
This is where my understanding of Obj-C breaks down. Feels to me like this should be easier but I'm still confused.
Now if initialValue was an NSString with the value of #"12" maybe it is? then self.value = [initialValue integerValue];
and I could build image with multiple stringByAppendingString calls. Just seems tedious for something so simple in other languages.
What's best way to handle something like this?
Your init method looks fine, and if you're only dealing with integer values, you do want to use NSInteger as opposed to NSString or even NSNumber. The one thing you might want to change is the parameter you're passing should be (NSInteger), not (NSInteger *) - an NSInteger is not an object, but in Objective-C is a primitive type similar to int.
You can build the image name by using NSString's stringWithFormat: selector, as such:
NSInteger myInteger = 12;
NSString *imageString = [NSString stringWithFormat:#"image%d.png", myInteger];
Just to expand on what Tim mentioned, an NSInteger is actually just a #define. On 32-bit architectures, it's equivalent to an int and on 64-bit architectures it's a long.
If you need to treat an integer as an object (to put it in an NSDictionary, for instance), you can wrap it in an instance of NSNumber using numberWithInteger:.
Hope that helps!
One other thing to keep in mind though is that when you're dealing with paths NSString gives you stringByAppendingPathExtension: and stringByAppendingPathComponent:, both of which handle things like trailing slashes better than if you just use stringByAppendingString:.
If you describe a little more about what this object does and what kind of images it's loading I might be able to offer some advice if I can think of a better way of creating the filenames, instead of just passing a number around.

Creating an NSArray initialized with count N, all of the same object

I want to create an NSArray with objects of the same value (say NSNumber all initialized to 1) but the count is based on another variable. There doesn't seem to be a way to do this with any of the intializers for NSArray except for one that deals with C-style array.
Any idea if there is a short way to do this?
This is what I am looking for:
NSArray *array = [[NSArray alloc] initWithObject:[NSNumber numberWithInt:0]
count:anIntVariable];
NSNumber is just one example here, it could essentially be any NSObject.
The tightest code I've been able to write for this is:
id numbers[n];
for (int x = 0; x < n; ++x)
numbers[x] = [NSNumber numberWithInt:0];
id array = [NSArray arrayWithObjects:numbers count:n];
This works because you can create runtime length determined C-arrays with C99 which Xcode uses by default.
If they are all the same value, you could also use memset (though the cast to int is naughty):
id numbers[n];
memset(numbers, (int)[NSNumber numberWithInt:0], n);
id array = [NSArray arrayWithObjects:numbers count:n];
If you know how many objects you need, then this code should work, though I haven't tested it:
id array = [NSArray arrayWithObjects:(id[5]){[NSNumber numberWithInt:0]} count:5];
I can't see any reason why this structure in a non-mutable format would be useful, but I am certain that you have your reasons.
I don't think that you have any choice but to use a NSMutableArray, build it with a for loop, and if it's really important that the result not be mutable, construct a NSArray and use arrayWithArray:
I agree with #mmc, make sure you have a valid reason to have such a structure (instead of just using the same object N times), but I'll assume you do.
There is another way to construct an immutable array which would be slightly faster, but it requires creating a C array of objects and passing it to NSArray's +arrayWithObject:count: method (which returns an autoreleased array, mind you) as follows:
id anObject = [NSNumber numberWithInt:0];
id* buffer = (id*) malloc(sizeof(id) * anIntVariable);
for (int i = 0; i < anIntVariable; i++)
buffer[i] = anObject;
NSArray* array = [NSArray arrayWithObjects:buffer count:anIntVariable];
free(buffer);
You could accomplish the same thing with even trickier pointer math, but the gains are fairly trivial. Comment if you're interested anyway.
Probably the reason there is no such method on NSArray is that the semantics are not well defined. For your case, with an immutable NSNumber, then all the different semantics are equivalent, but imagine if the object you were adding was a mutable object, like NSMutableString for example.
There are three different semantics:
retain — You'd end up with ten pointers to the same mutable string, and changing any one would change all ten.
copy — You'd end up with ten pointers to the same immutable string, or possibly ten different pointers to immeduable strings with the same value, but either way you'd not be able to change any of them.
mutableCopy — You'd end up with ten different mutable string objects, any of which you could change independently.
So Apple could write three variants of the method, or have some sort of parameter to control the semantics, both of which are ugly, so instead they left it to you to write the code. If you want, you can add it as an NSArray category method, just be sure you understand the semantic options and make it clear.
The method:
-(id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
has this same issue.
Quinn's solution using arrayWithObjects:count: is a reasonably good one, probably about the best you can get for the general case. Put it in an NSArray category and that's about as good as it is going to get.