NSArray of structures? - objective-c

Trying to implement the following structure from "c" to use NSArray in objective-c:
In standard-c:
struct structDog{
char *name;
int age;
};
struct structLitter{
struct structDog puppy[10];
};
Then I use malloc to allocate space.
But since I am using NSArray.
But in Objective-c I am using NSArray... so ???
NSArray struct structDog *puppy; // <<---this doesn't work
thanks

Assuming that you are trying to do is get your struct into your NSArray you need to use NSValue. For instance you can do something like:
NSArray* myArray = [NSArray arrayWithObjects:[NSValue valueWithPointer: myDog],
[NSValue valueWithPointer: myPuppy],
nil];
structDog* dog = (structDog*)[[myArray objectAtIndex:0] pointerValue];

Related

Objective-C equivalent of 'tuple unpacking'

Sometimes, I get sad when I can't use Python. In Python, I handle an array of arguments, unpacking them as such:
name, handle, parent_handle, [left, top, right, bottom], showing, scrollable = data
I must now do the same in Objective-C, with NSArrays. Am I doomed to 11 lines of:
NSString *name = (NSString *)[data objectAtIndex:0];
NSNumber *handle = (NSNumber *)[data objectAtIndex:1];
//....
or is there a better way?
Yes. You are doomed. DOOMED! Mwah ha ha ha ha!
You can omit the casts and use subscripting to make it a little bit shorter though:
NSString *name = data[0];
NSNumber *handle = data[1];
// ...
You can omit the casts because both objectAtIndex: and subscripting return type id, which can be converted to any Objective-C class type without casting.

How to add all decimal numbers in an NSMutableArray

I have a NSMutableArray which have some NSDecimalNumber in it, like (500,50.80,70,8000)
Now I want to add all those decimal numbers together.
I've tried to use
for (NSDecimalNumber *number in self.numbersArray)
{
NSDecimal *sum += [number decimalValue]
}
But failed.
A simple way to add all NSNumbers in an array is (similar to what #Mahonor said in a comment):
NSArray *myArray = ... // array of NSNumber (or NSDecimalNumber) objects
NSNumber *sum = [myArray valueForKeyPath:#"#sum.self"];
Contrary to what the Collection Operators: sum states, the numbers in the array are not converted to double, but to NSDecimal. Therefore, no precision is lost when adding decimal numbers. Even NSNumber objects which are not decimal numbers are converted to NSDecimal for the addition. The result of the summation is an instance of NSDecimalValue.
I verified (or tried to) that in two different ways. First, I ran this code
NSNumber *a = [NSNumber numberWithDouble:1.2];
NSNumber *b = [NSDecimalNumber decimalNumberWithString:#"-5.7"];
NSArray *myArray = #[a, b];
id sum = [myArray valueForKeyPath:#"#sum.self"];
and activated Objective-C message logging by setting the environment variable "NSObjCMessageLoggingEnabled=YES". As can be seen in the created "/tmp/msgSends-NNNN" file, decimalNumber (and not doubleValue) is sent to both number objects.
Second, I created a custom class implementing both decimalValue and doubleValue, and applied #sum.self to an array of objects of the custom class:
#interface MyClass : NSObject
#property (nonatomic, assign) double value;
#end
#implementation MyClass
- (NSDecimal)decimalValue
{
return [[NSNumber numberWithDouble:self.value] decimalValue];
}
- (double)doubleValue
{
return self.value;
}
#end
MyClass *a = [MyClass new]; a.value = 1.2;
MyClass *b = [MyClass new]; b.value = -5.7;
NSArray *myArray = #[a, b];
id sum = [myArray valueForKeyPath:#"#sum.self"];
By setting breakpoints in both methods, it is seen that only decimalValue is used for the summation (and valueForKeyPath:#"#sum.self" throws an exception if the class does not implement decimalValue).
One can also see that decimalValue is called from
-[NSArray(NSKeyValueCoding) _sumForKeyPath:]
and the assembler code for this method shows that NSDecimalAdd is uses to add the numbers.
Use - (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber
Take a look at NSDecimalNumber Class Reference
NSDecimalNumber *lNumber = [NSDecimalNumber zero];
for (NSDecimalNumber *number in self.numbersArray)
{
lNumber = [lNumber decimalNumberByAdding:number];
}
Manohar's suggestion in the comments is not bad. You can indeed use KVC collection operators to make a one-liner out of this: [myArray valueForKeyPath:#"#sum.doubleValue"];, but you potentially lose precision (depending on the numbers you have stored).
You're basically looking for "reduce" functionality; you need to chain calls to decimalNumberByAdding: so that each call has the succeeding element of the array as its argument. Doing this on an NSArray is easy enough, using performSelector:withObject:
#implementation NSArray (Reduce)
- (id)reduceUsingSelector: (SEL)sel
{
id res = [self objectAtIndex:0];
for( id obj in [self subarrayWithRange:(NSRange){1, [self count]-1}] ){
res = [res performSelector:sel withObject:obj];
}
return res;
}
#end
Use this like so: NSDecimalNumber * sum = [myArray reduceUsingSelector:#selector(decimalNumberByAdding:)];
The code you have isn't successful because NSDecimal is a struct, not an object; it shouldn't be declared as a pointer, and if it wasn't, you wouldn't be able to add it. That's not the right route to a solution.

How to use bridge for CLLocationCoordinate2D

AM trying to add CLLocationCoordinate2D[] to NSMutableArray and send it as parameter. But (__bridge id) is crashing the app. Struct to id conversion is the problem. Could anyone please let me know how to use this please.
CLLocationCoordinate2D coordinates[1000];
coordinates[index] --- all the coordinates added to it in loop.
NSMutableArray *coorArray = [NSMutableArray array];
[coorArray addObject:(__bridge id)(coordinates)]; crashes here
Use:
NSMutableArray *coorArray = [NSMutableArray array];
[coorArray addObject:[NSValue valueWithPointer:coordinates]];
Then when you want to retrieve the array of struct:
CLLocationCoordinate2D coordinates[] = [coorArray objectAtIndex:0].pointerValue;
A C array is not an object, so it can't be bridged.
You should look at +[NSValue valueWithBytes:objCType:] not a (__bridge) cast. Bridge is for other things.
e.g.:
[NSValue value:&coordinate withObjCType:#encode(CLLocationCoordinate2D)];
I guess it's possible to encode whole array too

Array of enums - convert to NSArray

Having
enum {MyA, MyB, Null};
typedef NSNumber myEnum;
Or
typedef enum {MyA, MyB, Null} myEnum;
1) How do I create an array
myEnum* myEnumTemp[] = {MyA, MyB};
Just gives "Implicit conversion of 'int' to NSNumber* is disallowed with ARC(ref. counting)
2) If you are able to create an array how to convert it to NSArray?
Try to do it this way :
typedef enum { MyA, MyB, Null } myEnum;
Then, to create an array, wrap the numbers into NSNumbers objects :
NSArray *a = [NSArray arrayWithObjects:[NSNumber numberWithInteger:MyA],
[NSNumber numberWithInteger:MyB],
nil];
In Obj C:
enumArray = #[#(enum1),#(enum2)];
In Swift:
enumArray = NSArray(objects: enum1.rawValue, enum2.rawValue);
Basically, you need to wrap the value in a NSNumber object.
#define INT_OBJ(x) [NSNumber numberWithInt:x]
[array addObject:INT_OBJ(MyA)];
And there was nothing wrong with your other array, you just should have defined it like this:
typedef enum {MyA, MyB, Null} myEnum;
myEnum values[] = { MyA, MyB };
The problem was that you defined myEnum as a NSNumber, which is not equal to an enum value (int).

Pointers in method params - objective-c

How to pass pointer as param in method?
for example:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray *) pointerToArray;
where simpleString is simple param, and pointerToArray is pointer to an array;
In Objective-C, strings and arrays are both classes. As you can see, they are already accessed through pointers. So you simply use them as the declaration says:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray *) pointerToArray;
And you invoke like:
NSString *s = #"Hello, world";
NSMutableArray *a = [NSMutableArray arrayWithObjects: #"Hello", #"silly", #"example", nil];
[yourClass dosomething:s :a];
FWIW, the name of your method is dosomething::. It is customary to denote each parameter, so I would call it:
-(void) doSomethingWithString:(NSString *)greeting array:(NSMutableArray *)strings;
then the name is doSomethingWithString:array: which is much more readable, IMO. You
invoke it with:
[yourClass doSomethingWithString:s array:a];
Like this:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray **) pointerToArray;
(Add a second '*' to the parameter type
In your method, you then do something like:
*pointerToArray = [NSMutableArray array];
For example:
NSString *localSimpleString;
NSMutableArray *localArray;
[self dosomething:localSimpleString :pointerToArray];