Storing and retrieving C struct in NSMutable Array - iphone

I have the following C Struct defined :
typedef struct {
float x;
float y;
float z;
} metric;
I want to store accelerometer points in an array and do something with it later.
So I have a mutable array that is initialized like so in my viewDidLoad method :
metricsArray_ = [[[NSMutableArray alloc] init]retain];
in my didAccelerate method I create a struct to store the values and add it to the array :
metric metricData;
metricData.x = acceleration.x;
metricData.y = acceleration.y;
metricData.z = acceleration.z;
NSValue *metricObject = [[NSValue valueWithBytes:&metricData objCType:#encode(struct metric)]retain];
[metricsArray_ addObject:metricObject];
Later when i stop polling for data I try iterate over the array and log the values but the values are all zero :
NSEnumerator * enumerator = [metricsArray_ objectEnumerator];
id element;
while(element = [enumerator nextObject])
{
metric metricData;
[element getValue:&metricData];
NSLog(#"x=%f,y=%f,z=%f",metricData.x, metricData.y,metricData.z);
}
What am I doing wrong here ? And is there a better way to store accelerameter data points? I wanted to store them in a struct rather than an object ( although you end up having to use NSValue anyways )
Thanks

My initial thought is: why do you need a metrics struct if you just use it for x/y/z values. There already is an object for that: UIAcceleration.
I have a game that stores instances of UIAcceleration in an NSMutableArray about 60 times per second as they are received in the accelerometer:didAccelerate: callback. Zero performance issues.
(I am using the NSMutableArray as a 'ring buffer'. When more than N obejcts have been added, items are removed from the beginning. SO that I just capture the last N seconds)

I couldn't even compile your code above. I had to make the following change of removing the struct in the #encode(). This then seemed to compile and work.
NSValue *metricObject = [NSValue valueWithBytes:&metricData objCType:#encode(metric)];
There also appears to be an unnecessary retain.

Related

Core Data Boolean property NSNumber doesn't remember it's boolean

I have a model with a property that looks like this:
When I set its value, for example:
model.isResolved = #YES;
The NSNumber that's kept inside the model "forgets" that it's a boolean:
NSLog(#"%#", strcmp([self.isResolved objCType], #encode(BOOL)) == 0 ? #"equal" : #"different");
Prints "different". What is up with that?
What is up with that?
From the documentation:
Note that number objects do not necessarily preserve the type they are created with.
That's another inconsistency-for-optimization in Cocoa.
Core Data dynamically generates getter and setter methods for all attributes (and relationships) of managed object classes. These accessor methods are different from the "usual" #synthesized accessor methods which are backed up by an instance variable.
In particular, if you set an attribute and then retrieve the attributes value again, you can get an object that is different from the "original" object. The following test shows this, foo1 is an instance of a Core Data entity with the Boolean attribute "show":
NSNumber *yes = #YES;
NSLog(#"yes = %p, type = %s", yes, [yes objCType]);
foo1.show = yes;
NSNumber *val = foo1.show;
NSLog(#"val = %p, type = %s", val, [val objCType]);
Output:
yes = 0x16e595c, type = c
val = 0x744c150, type = i
So even if you set the attribute to a c = char encoded number, the getter method returns a i = int encoded number.
This test was done in the iOS Simulator. Interestingly the same test, running on OS X 64-bit, returns a c = char encoded number.
Therefore, the actual encoding of Boolean and other scalar attributes in Core Data objects should probably be treated as an implementation detail of Core Data.
If you need to check the Core Data type as defined in the model, you can use the objects entity description instead of objCType:
NSEntityDescription *entity = [foo1 entity];
NSAttributeDescription *attr = [[entity attributesByName] objectForKey:#"show"];
NSAttributeType type = [attr attributeType];
if (type == NSBooleanAttributeType) {
NSLog(#"Its a Boolean!");
}
Its stored as a NSNumber - by the way #YES is creating a NSNumber like
[NSNumber numberWithBool:YES]
so to get the bool back out you do:
[isResolved boolValue]
(you can avoid this by ticking the Use Scalar Properties when you create your models)

How to return a array list of non-object type in Objective-C

I have a method which should return a list of non-object types. Specifically a list of CLLocationCoordinate2D. I would like to have it as a list and not as an NSArray I use the outcome to create a MKPolyLine using
+ (MKPolyline *)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count
With the code below I get an "Array initializer must be an initializer" list.
-(CLLocationCoordinate2D[])pathBetween:(CLLocationCoordinate2D)start and:(CLLocationCoordinate2D)end withNumberofPoints:(int)nrOfPoints{
CLLocationCoordinate2D returnPath[nrOfPoints];
for (int i=0; 1<nrOfPoints; i++) {
float fraction=i/(nrOfPoints);
CLLocationCoordinate2D coord=[self coordinateAtFraction:fraction between:start andEnd:end forAccuracy:.02];
returnPath[i]=coord;
}
return returnPath;
}
If I initialize the list using
CLLocationCoordinate2D returnPath[nrOfPoints]={};
I get "Variable sized object may not be initialized".
If I remove the [] from the method I get "returning 'CLClocationCoordinate2D[nrOfPoints]' from a function with incompatible result type 'CLLocationCoordinate2D'"
Any Ideas?
You are trying to create the array on the stack and then return it. That won't work.
There are several things you can do here:
malloc the array of points.
returnPath = malloc(sizeof(CLLocationCoordinate2D) * nrOfPoints;
This would mean the caller would have to free the returned pointer.
return the array in an NSData
NSMutableData* returnData = [[NSMutableData alloc] initWithLength: sizeof(CLLocationCoordinate2D) * nrOfPoints];
returnPath = [returnData mutableBytes];
// the other stuff
return [returnData autorelease];
This means that you don't have to worry about memory management any more than for any other Objective-C object.
Have the caller supply the array e.g.
-(void) getPath: (CLLocationCoordinate2D*) returnPath // caller allocates a big enough buffer
between: (CLLocationCoordinate2D)start
and: (CLLocationCoordinate2D)end
withNumberofPoints:(int)nrOfPoints
{
// code as question but no allocation or return value
}
CLLocationCoordinate2D returnPath[nrOfPoints]; value for noOfPoints should be available at complile time. if u replace line with CLLocationCoordinate2D *returnPath = (CLLocationCoordinate2D *) malloc(nrOfPoints * sizeof (CLLocationCoordinate2D));
array would be available at run time. But I am not sure sizeof method will return the object size.
As pointed out by Kevin in the comment: you have to do something about the fact that your return argument is a local variable. The reference "returnPath" will point into Nirvana outside your function. Here is a video that explains what happens there, enjoy: http://www.youtube.com/watch?v=6pmWojisM_E
For the array of CLLocationCoordinate2D, look at this post here:
NSMutableArray of ClLocationCoordinate2D
Hope this helps.

How to pass an array to a method and then determine the array's size?

I have this method:
+ (NSData *) createWave: (short[])sampleData {
int i = [sampleData count]; // Warning: Invalid receiver type 'short int *'
}
Inside this method, I'm trying to determine how many elements are in the samples array that was passed in. But I'm getting the warning above (I get the same warning if I change samples to short *).
How can I pass an array like this, and then determine the array's size?
You can't.
Either make sure that the last element in your array is unique and check for that or pass in a size parameter as well i.e.
+ (NSData *) createWave:(short [])samples size:(size_t)count {
int i = count;
}
short[] isn't an object so you can't call methods on it - that's why you're getting a warning (and probably a crash if you run the code!)
You are trying to use a C style array as a parameter and then access it as an Objective-C object. (I am assuming sampleData and samples are supposed to be the same). Use an NSArray of NSNumbers instead because with C style arrays you need to know the length.
+ (NSData *) createWave: (NSArray*)sampleData {
int i = [sampleData count];
}

NSDictionary with key => String and Value => c-Style array

I need an NSDictionary which has key in the form of string (#"key1", #"key2") and value in the form of a C-style two-dimensional array (valueArray1,valueArray2) where valueArray1 is defined as :
int valueArray1[8][3] = { {25,10,65},{50,30,75},{60,45,80},{75,60,10},
{10,70,80},{90,30,80},{20,15,90},{20,20,15} };
And same for valueArray2.
My aim is given an NSString i need to fetch the corresponding two-dimensional array.
I guess using an NSArray, instead of c-style array, will work but then i cannot initialize the arrays as done above (i have many such arrays). If, however, that is doable please let me know how.
Currently the following is giving a warning "Passing argument 1 of 'dictionaryWithObjectsAndKeys:' from incompatible pointer type" :
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:valueArray1,#"key1",
valueArray2,#"key2",nil];
Is valueArray2 also an int[][3]? If so, you could use
[NSValue valueWithPointer:valueArray1]
to convert the array into an ObjC value. To retrieve the content, you need to use
void* valuePtr = [[myDict objectForKey:#"key1"] pointerValue];
int(*valueArr)[3] = valuePtr;
// use valueArr as valueArrayX.
If there's just 2 keys, it is more efficient to use a function like
int(*getMyValueArr(NSString* key))[3] {
if ([key isEqualToString:#"key1"]) return valueArray1;
else return valueArray2;
}
Rather than Adding Array Directly as a value in NSDictionary make a custom class in which create variable of NSArray ... and set this class object as value like
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:MyClassObj1,#"key1",
MyClassObj2,#"key2",nil];
where MyClassObj1 and MyClassObj2 are member of MyClass

What kind of data is in an "enum" type constant? How to add it to an NSArray?

What kind of information is stored behind such an enum type thing? Example:
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
I am not sure if I can safely add such an enum constant to an array. Any idea?
Enums in Objective-C are exactly the same as those in C. Each item in your enum is automatically given an integer value, by default starting with zero.
For the example you provided: UIViewAnimationCurveEaseInOut would be 0; UIViewAnimationCurveEaseIn would be 1, and so on.
You can specify the value for the enum if required:
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn = 0,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
This result of this would be: UIViewAnimationCurveEaseInOut is 0; UIViewAnimationCurveEaseIn is 0; UIViewAnimationCurveEaseOut is 1; and so on. However, for basic purposes you shouldn't need to do anything like that; it just gives you some useful info to toy with.
It should be noted based on the above, that an enum can't assume to be a unique value; different enum identifiers can be equal in value to each other.
Adding an enum item to a NSArray is as simple as adding an integer. The only difference would be that you use the enum identifer instead.
[myArray addObject:[NSNumber numberWithInt:UIViewAnimationCurveEaseInOut]];
You can check this out for yourself by simply outputting each enum to the console and checking the value it provides you with. This gives you the opportunity to investigate the details of how it operates. But for the most part you won't really need to know on a day to day basis.
Enums are typically int values. You can store them in an array by wrapping them in an NSNumber:
[myMutableArray addObject:[NSNumber numberWithInt:myAnimationCurve]];
... then get them back out like this:
UIViewAnimationCurve myAnimationCurve = [[myMutableArray lastObject] intValue];
Enums in Objective-C are the same as enums in vanilla C. It's just an int. If you're using an NSArray, then it expects a pointer and you'll get a warning if you try to add an int to it:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObject:UIViewAnimationCurveEaseInOut];
// Last line results in:
// warning: passing argument 1 of 'addObject:' makes
// pointer from integer without a cast
If you're storing a large collection of 32-bit integers, consider using the appropriate CF collection type rather than the NS collection type. These allow you to pass in custom retain methods, which gets rid of the need to box every integer added to the collection.
For example, let's say you want a straight array of 32-bit ints. Use:
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
The last parameter tells the array to not retain/release the "addresses" you pass in to it. So when you do something like this:
CFArrayAppendValue(arrayRef, 1);
What the array thinks is that you're passing in a pointer to an object living at the memory address 0x1. But since you told it to not call retain/release on that pointer, it gets treated as a standard int by the collection.
FWIW, for educational value, standard NSMutableArrays have equivalent CF types. Through toll-free bridging you can use the CF collection as a standard Foundation collection:
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, kCFTypeArrayCallbacks);
NSMutableArray *array = (NSMutableArray *)arrayRef;
[array addObject:#"hi there!"];
NSLog(#"%#", [array objectAtIndex:0]); // prints "hi there!"
You can apply the same tricks to dictionaries (with CFDictionary/CFMutableDictionary), sets (CFSet/CFMutableSet), etc.