32/64 bit CGPoint Persistence in iOS across devices - ios

I've inherited some freehand drawing code which records a CGPathRef of points that are then converted to a collection of CGPoints which eventually get saved within our core data DB as NSData.
Current conversion code looks like this:
#interface StoredPath : NSObject
{
CGPathRef path;
}
#implementation StoredPath
- (NSArray *)getPoints
{
// Convert path to an array
NSMutableArray *a = [NSMutableArray arrayWithObject:[NSNumber numberWithBool:YES]];
CGPathApply(path, (__bridge void *)(a), saveApplier);
if (![[a objectAtIndex:0] boolValue])
{
return nil;
}
[a removeObjectAtIndex:0];
return (a);
}
static void saveApplier(void *info, const CGPathElement *element)
{
NSMutableArray *a = (__bridge NSMutableArray*) info;
int nPoints;
switch (element->type)
{
case kCGPathElementMoveToPoint:
nPoints = 1;
break;
case kCGPathElementAddLineToPoint:
nPoints = 1;
break;
case kCGPathElementAddQuadCurveToPoint:
nPoints = 2;
break;
case kCGPathElementAddCurveToPoint:
nPoints = 3;
break;
case kCGPathElementCloseSubpath:
nPoints = 0;
break;
default:
[a replaceObjectAtIndex:0 withObject:[NSNumber numberWithBool:NO]];
return;
}
NSNumber *type = [NSNumber numberWithInt:element->type];
NSData *points = [NSData dataWithBytes:element->points length:nPoints*sizeof(CGPoint)];
[a addObject:[NSDictionary dictionaryWithObjectsAndKeys:type, #"type", points, #"points", nil]];
}
#end
The important part to notice here is the line:
NSData *points = [NSData dataWithBytes:element->points length:nPoints*sizeof(CGPoint)];
It preserves the CGPoint data into a compact NSData object, which then gets saved in our core data DB.
This was fine when the app's data only lived on a single device. But now that this data is synced to other devices, it breaks down due to CGFloat (makes up CGPoint struct) being 32bit or 64bit on various iOS devices.
When the 32bit NSData of CGPoints is read on a 64bit iOS device, the bytes don't quite align and the drawing gets mangled. The same of course happens in vice-versa (64bit NSData of CGPoints is read on a 32bit iOS device).
I've been struggling to refactor this saveApplier: method in a way that is platform independent and can be easily saved in core data.
Update:
I'm considering packing the points into a "|" separated string.
example: pointsToSave = {442, 797.5}|{442, 797.5}|{442, 797.5}
NSString *pointsToSave = [[NSString alloc] init];
// Convert CGPoint's to a "|" separated string.
for (int i=0; i<nPoints; i++)
{
pointsToSave = [pointsToSave stringByAppendingString:NSStringFromCGPoint(element->points[i])];
if ((i+1)<nPoints)
{
pointsToSave = [pointsToSave stringByAppendingString:#"|"];
}
}
It makes it fairly simple to parse between platforms as well.
Thoughts?

You could pick either float or double as the data type to save with. This will explicitly set the size, since float is 32-bit and double is 64-bit.
For example, if you want more compactness, you could do this:
float *floats = [self pointsToFloats:element->points];
NSData *points = [NSData dataWithBytes:floats length:nPoints*sizeof(float)*2];
free(floats); // Note will need to free, since assuming pointsToFloats uses malloc. Use delete if you use new.
Where pointsToFloats would be defined something like:
- (float *)pointsToFloats:(CGPoint *)points; // Assuming this is straight-forward enough to implement
This also means you'll need something to unpack the data too.
- (CGPoint *)floatsToPoints:(float *)floats;
You can also use double if you need more precision.
There are of course others ways of doing this too.

Related

Modify C-array in objective-c function

As an iOS programmer I sometimes delve into C to achieve faster results (well actually for fun) and I'm struggling to modify a C-array's values inside a function call. Although I think this answer may help (Modifying a array in a function in C), I don't know where to begin in implementing it.
Currently I have:
[object.guestlists enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
NSObject *someNSObject = [NSObject new];
NSInteger array[3] = {totalIncomingMales,totalIncomingFemales,
totalIncomingGuestsCount};
[self callMethod:object withCArray:cArray];
}];
- (void) callMethod:(NSObject*) object withCArray:(NSInteger*) cArray {
// do something with that object
NSInteger totalIncomingMales = cArray[0],
totalIncomingFemales = cArray[1],
totalIncomingGuestsCount = cArray[2];
// modify these integers internally so that the C-array passed in is also modified
}
Obviously this passes a pointer and therefore doesn't modify the value. I tried replacing the NSinteger * with NSInteger ** and making,
e.g. totalIncomingMales = * cArray[0], but alas I wasn't able to pass the c-array as a parameter (even with an ampersand).
Some useful material and potentially a solution to this would be much appreciated!
Not sure if I understand your question, but it seems to be trivial one:
- (void) callMethod:(NSObject*) object withCArray:(NSInteger*) cArray {
// modify these integers internally so that the C-array passed in is also modified
cArray[0] = 10;
cArray[1] = 20;
cArray[2] = 30;
}
- (void) myMethod:(NSString *) myConstString array: (NSArray *) myArray
{
NSInteger array[3] = {1,2,3};
NSLog(#"%ld %ld %ld", (long)array[0],(long)array[1],(long)array[2]);
[self callMethod:nil withCArray:array];
NSLog(#"%ld %ld %ld", (long)array[0],(long)array[1],(long)array[2]);
}
result will be:
1,2,3
after 10,20,30. No pointer trickery needed, because you are telling it is NSInteger so compiler does it for you.

How to randomize letters correctly from an NSString

I am creating a word scrambler and I am having issues randomizing the letters. When the letters get randomized, it doesn't make sense.
For example, the word PARK shows as AAPA. So, as you can tell it won't make sense for the user when it is time to unscramble.
Just so you know, I am using a .plist file to hold the words.
This is the code I am using to randomize the letters:
_words = [NSMutableArray arrayWithCapacity:scramblelength];
for (int i=0;i<scramblelength;i++) {
NSString *letter = [scramble substringWithRange:[scramble rangeOfComposedCharacterSequenceAtIndex:arc4random()%[scramble length]]];
Then, I am creating UIImageViews to display the scrambled words:
if (![letter isEqualToString:#""]) {
GameView *boxes = [[GameView alloc] initWithLetter:letter andSideLength:boxSide];
boxes.center = CGPointMake(xOffset + i*(boxSide + kTileMargin), kScreenHeight/4*3);
[self.scrambleView addSubview:boxes];
[_words addObject:boxes];
What am I doing wrong here? I would like for the letters in the scrambled words to make sense.
Please help, I am stuck on this one!
Thanks!
As long as your string length will fit in 32 bits, this should be fine. If not, I would replace arc4random_uniform with a uniform random number generator in C++ and compile this as an Objective-C++ module.
The code simply iterates through the string, and swaps each composed character sequence with some random composed character sequence from the same string.
Sorry, that's what happens when you are arrogant and just type out code. Let me know if you have trouble with this one...
For much larger strings, there is a more efficient way, but this seems to do the trick.
NSMutableString category...
#interface NSMutableString (Scramble)
- (void)scramble;
#end
#implementation NSMutableString (Scramble)
static void
swapRanges(NSMutableString *string, NSRange iRange, NSRange jRange)
{
// Need to replace the "trailing" component first
if (NSEqualRanges(iRange, jRange)) return;
if (iRange.location > jRange.location) {
NSRange tmpRange = iRange;
iRange = jRange;
jRange = tmpRange;
}
NSString *iString = [self substringWithRange:iRange];
NSString *jString = [self substringWithRange:jRange];
[string replaceCharactersInRange:jRange withString:iString];
[string replaceCharactersInRange:iRange withString:jString];
}
- (void)scramble
{
for (NSUInteger i = 0; i < self.length; ++i) {
NSRange iRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
NSUInteger j = arc4random_uniform(self.length);
NSRange jRange = [self rangeOfComposedCharacterSequenceAtIndex:j];
swapRanges(self, iRange, jRange);
}
}
#end
NSString category...
#interface NSString (Scramble)
- (NSString*)scrambledString;
#end
#implementation NSString (Scramble)
- (NSString *)scrambledString
{
NSMutableString *result = [self mutableCopy];
[result scramble];
return [result copy];
}
#end
Sample use...
[someMutableString scramble];
NSString *mixedUp = [someString scrambledString];
Or, if you are comfortable with C++, convert to a std::wstring, call std::random_shuffle, then convert that to a NSString. Lots less bugs when using proven, well tested code.
When you are getting a random letter, you need to do something to remove that letter from your NSMutableArray (ie the word's letters when in order). So as you iterate through the word, each time there are fewer characters remaining. Right now, from your limited code block (the first one), it appears you might not be doing that. You want something like "[_words removeObjectAtIndex:letterIndex]" and you would also want to iterate from number of letters down to zero as you remove items from the array also: for (int i=[_words count]; i > [_words count]; i--) because you need to go from 4 letters down to 0 letters left.
So, I'm sure there are more efficient ways to do this, but I go by the rule of not optimizing until you need to. With that in mind, this code appears to work correctly:
- (NSString *)scrambleWord:(NSString *)word {
NSMutableArray *letterArray = [self letterArrayFromWord:word];
NSMutableString *returnValue = [[NSMutableString alloc] init];
do {
int randomIndex = arc4random() % letterArray.count;
[returnValue appendString:letterArray[randomIndex]];
[letterArray removeObjectAtIndex:randomIndex];
if (letterArray.count == 1) {
[returnValue appendString:letterArray[0]];
break;
}
} while (YES);
if ([[returnValue copy] isEqualToString:word]) {
return [self scrambleWord:word];
} else {
return [returnValue copy];
}
}
- (NSMutableArray *)letterArrayFromWord:(NSString *)word {
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < word.length; i = i + 1) {
[array addObject:[NSString stringWithFormat:#"%C", [word characterAtIndex:i]]];
}
return array;
}

How does NSMutableArray achieve such high speed in fast enumeration

The y axis represents the the average access time (in ns) to each node in the list/array (total time to access all elements divided by the number of elements).
The x axis represents the number of elements in the array being iterated over.
Where red is an implementation of NSMutableArray and blue is my linked list (CHTape).
In each outer loop each list/array has a empty string #"" appended to it. In the inner loops each string in each list/array is retrieved, this is timed and recorded. After everything the times our outputted in a Wolfram Language output to produce a plot.
How does NSMutableArray achieve such amazing and consistent results? How can one achieve similar?
My NSFastEnumeration Implementation:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])stackBuffer count:(NSUInteger)len
{
if (state->state == 0)
{
state->state = 1;
state->mutationsPtr = &state->extra[1];
state->extra[0] = (unsigned long)head;
}
CHTapeNode *cursor = (__bridge CHTapeNode *)((void *)state->extra[0]);
NSUInteger i = 0;
while ( cursor != nil && i < len )
{
stackBuffer[i] = cursor->payload;
cursor = cursor->next;
i++;
}
state->extra[0] = (unsigned long)cursor;
state->itemsPtr = stackBuffer;
return i;
}
Complete Testing Code:
NSMutableArray *array = [NSMutableArray array];
CHTape *tape = [CHTape tape];
unsigned long long start;
unsigned long long tapeDur;
unsigned long long arrayDur;
NSMutableString * tapeResult = [NSMutableString stringWithString:#"{"];
NSMutableString * arrayResult = [NSMutableString stringWithString:#"{"];
NSString *string;
int iterations = 10000;
for (int i = 0; i <= iterations; i++)
{
[tape appendObject:#""];
[array addObject:#""];
// CHTape
start = mach_absolute_time();
for (string in tape){}
tapeDur = mach_absolute_time() - start;
// NSArray
start = mach_absolute_time();
for (string in array){}
arrayDur = mach_absolute_time() - start;
// Results
[tapeResult appendFormat:#"{%d, %lld}", i, (tapeDur/[tape count])];
[arrayResult appendFormat:#"{%d, %lld}", i, (arrayDur/[array count])];
if ( i != iterations)
{
[tapeResult appendString:#","];
[arrayResult appendString:#","];
}
}
[tapeResult appendString:#"}"];
[arrayResult appendString:#"}"];
NSString *plot = [NSString stringWithFormat:#"ListPlot[{%#, %#}]", tapeResult, arrayResult];
NSLog(#"%#", plot);
By forcing ARC off on the link list related files efficiency increased dramatically. It reduced access time from ~70ns to ~14ns. While this is still slower, on average, then NSArray its only, on average, about two times slower, as opposed to ten times slower.
While ARC can make some code faster, in iterative situations adds unnecessary release/retain calls.
Discovered thanks to Greg Parker's comment.

Finding smallest and biggest value in NSArray of NSNumbers

What's an effective and great way to compare all the values of NSArray that contains NSNumbers from floats to find the biggest one and the smallest one?
Any ideas how to do this nice and quick in Objective-C?
If execution speed (not programming speed) is important, then an explicit loop is the fastest. I made the following tests with an array of 1000000 random numbers:
Version 1: sort the array:
NSArray *sorted1 = [numbers sortedArrayUsingSelector:#selector(compare:)];
// 1.585 seconds
Version 2: Key-value coding, using "doubleValue":
NSNumber *max=[numbers valueForKeyPath:#"#max.doubleValue"];
NSNumber *min=[numbers valueForKeyPath:#"#min.doubleValue"];
// 0.778 seconds
Version 3: Key-value coding, using "self":
NSNumber *max=[numbers valueForKeyPath:#"#max.self"];
NSNumber *min=[numbers valueForKeyPath:#"#min.self"];
// 0.390 seconds
Version 4: Explicit loop:
float xmax = -MAXFLOAT;
float xmin = MAXFLOAT;
for (NSNumber *num in numbers) {
float x = num.floatValue;
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}
// 0.019 seconds
Version 5: Block enumeration:
__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
[numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
float x = num.floatValue;
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}];
// 0.024 seconds
The test program creates an array of 1000000 random numbers and then applies all sorting
techniques to the same array. The timings above are the output of one run, but I make about 20 runs with very similar results in each run. I also changed the order in which the 5 sorting methods are applied to exclude caching effects.
Update: I have now created a (hopefully) better test program. The full source code is here: https://gist.github.com/anonymous/5356982. The average times for sorting an
array of 1000000 random numbers are (in seconds, on an 3.1 GHz Core i5 iMac, release compile):
Sorting 1.404
KVO1 1.087
KVO2 0.367
Fast enum 0.017
Block enum 0.021
Update 2: As one can see, fast enumeration is faster than block enumeration (which is also stated here: http://blog.bignerdranch.com/2337-incremental-arrayification/).
EDIT: The following is completely wrong, because I forgot to initialize the object used as lock, as Hot Licks correctly noticed, so that no synchronization is done at all.
And with lock = [[NSObject alloc] init]; the concurrent enumeration is so slow
that I dare not to show the result. Perhaps a faster synchronization mechanism might
help ...)
This changes dramatically if you add the NSEnumerationConcurrent option to the
block enumeration:
__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
id lock;
[numbers enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
float x = num.floatValue;
#synchronized(lock) {
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}
}];
The timing here is
Concurrent enum 0.009
so it is about twice as fast as fast enumeration. The result is probably not representative
because it depends on the number of threads available. But interesting anyway! Note that I
have used the "easiest-to-use" synchronization method, which might not be the fastest.
Save float by wrapping under NSNumber then
NSNumber *max=[numberArray valueForKeyPath:#"#max.doubleValue"];
NSNumber *min=[numberArray valueForKeyPath:#"#min.doubleValue"];
*Not compiled and checked, already checked with intValue, not sure about double or float
sort it. take the first and the last element.
btw: you cant store floats in an NSArray, you will need to wrap them in NSNumber objects.
NSArray *numbers = #[#2.1, #8.1, #5.0, #.3];
numbers = [numbers sortedArrayUsingSelector:#selector(compare:)];
float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];
I agree with sorting the array then picking the first and last elements, but I find this solution more elegant (this will also work for non numeric objects by changing the comparison inside the block):
NSArray *unsortedArray = #[#(3), #(5), #(1)];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSNumber *item1 = (NSNumber *)obj1;
NSNumber *item2 = (NSNumber *)obj2;
return [item1 compare:item2];
}];
If you really want to get fancy and have a really long list and you don't want to block your main thread, this should work:
NSComparator comparison = ^NSComparisonResult(id obj1, id obj2) {
NSNumber *item1 = (NSNumber *)obj1;
NSNumber *item2 = (NSNumber *)obj2;
return [item1 compare:item2];
};
void(^asychSort)(void) = ^
{
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:comparison];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Finished Sorting");
//do your callback here
});
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), asychSort);
Made simple
NSArray *numbers = #[#2.1, #8.1, #5.0, #.3];
numbers = [numbers sortedArrayUsingSelector:#selector(compare:)];
float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];
NSLog(#"MIN%f",min);
NSLog(#"MAX%f",max);

NSDictionary loses data?

I've seen posts where NSDictionary loses data but in my case its a little wierd. I have a class that includes some data including NSString and NSIntegers and GLfloats. (As far as I know all of these do conform to NSCopying unlike things like CGFloat.)
I access the files quickly in my app and it all works, but then I try to access it again (to reload/refresh) the screen and then the NSInteger values return something between 180000, and a few billion for a value I know is definitely 4 so for some reason the data hasn't persisted.
It feels like the data is being lost/released/changed at some point but I don't know how it possibly can.
Edit:
Heres some of the code, this is where the texturedQuads are created and store, the method is called for each atlas I have in the init method
Also I have changed where I misused the word wrapper
-(void)loadAtlasData:(NSString *)atlasName
{
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
if (quadLibrary == nil)
quadLibrary = [[NSMutableDictionary alloc] init];
CGSize atlasSize = [self loadTextureImage:[atlasName
stringByAppendingPathExtension:#"png"]
materialKey:atlasName];
NSArray *itemData = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:atlasName ofType:#"plist"]];
for(NSDictionary *record in itemData)
{
TexturedQuad *quad = [self texturedQuadFromAtlasRecord:record
atlasSize:atlasSize
materialKey:atlasName];
[quadLibrary setObject:quad forKey:
[record objectForKey:#"name"]];
}
[apool release];
}
-(TexturedQuad *)texturedQuadFromAtlasRecord:(NSDictionary *)record
atlasSize:(CGSize)atlasSize
materialKey:(NSString *)key
{
TexturedQuad *quad = [[TexturedQuad alloc] init];
GLfloat xLocation = [[record objectForKey:#"xLocation"] floatValue];
GLfloat yLocation = [[record objectForKey:#"yLocation"] floatValue];
GLfloat width = [[record objectForKey:#"width"] floatValue];
GLfloat height = [[record objectForKey:#"height"] floatValue];
//find the normalized texture co-ordinates
GLfloat uMin = xLocation/atlasSize.width;
GLfloat vMin = yLocation/atlasSize.height;
GLfloat uMax = (xLocation + width)/atlasSize.width;
GLfloat vMax = (yLocation + height)/atlasSize.height;
quad.uvCoordinates[0] = uMin;
quad.uvCoordinates[1] = vMax;
quad.uvCoordinates[2] = uMax;
quad.uvCoordinates[3] = vMax;
quad.uvCoordinates[4] = uMin;
quad.uvCoordinates[5] = vMin;
quad.uvCoordinates[6] = uMax;
quad.uvCoordinates[7] = vMin;
quad.materialKey = key;
return [quad autorelease];
}
Second edit:
Added an example of the plist file
dict
(key)name
(string)sync
(key)xLocation
(integer)489
(key)yLocation
(integer)36
(key)width
(integer)21
(key)height
(integer)21
/dict
essentially its an array, consisting of dictionaries, each dictionary holds the data for the picture in the atlas. so theres the name, xLocation, yLocation, width, height.
edit 3:
Here is where I load the object from
I use a [MaterialController sharedMaterialController] to get an instance of this controller
-(TexturedQuad *)quadFromAtlasKey:(NSString *)atlasKey
{
return [quadLibrary objectForKey:atlasKey];
}
See if removing the auto-release pool sorts the issue. As far as I can see you don't need it as quadLibrary seems to be an iVar whilst itemData is auto-released by its parent class, as are all the TextureQuads you return in your for loop.

Resources