How to assign different data types to NSMutableData - nsmutabledata

I'm trying to create a vertex attribute array for storing vectors and indices in a mesh. I realise that vertex attributes can be made up from vector2, vector3 and/or vector4 so i'd like to create a class that caters for them all without constraining methods to return a certain type of object. As a test, i've written the following class making use of NSMutableData:
//VertexAttributeArray.h
#interface VertexAttributeArray : NSObject
{
NSMutableData *vertexData;
}
- (void *)data;
- (CGRect *)rect;
//VertexAttributeArray.m
- (id)init
{
self = [super init];
if(self)
{
vertexData = [[NSMutableData alloc] initWithLength:0];
}
return self;
}
- (void *)data
{
return [vertexData mutableBytes];
}
- (CGRect *)rect
{
return [vertexData mutableBytes];
}
- (void)dealloc
{
if(vertexData)
{
[vertexData release];
vertexData = nil;
}
[super dealloc];
}
From within my mesh class, i'd like to be able to call something like:
VertexAttributeArray *vertexAttributeArray = [[VertexAttributeArray alloc] init];
[vertexAttributeArray data][0] = CGRectMake(0.0, 0.0, 0.0, 0.0); //doesn't work - compiler error
[vertexAttributeArray rect][0] = CGRectMake(0.0, 0.0, 0.0, 0.0); //works fine
The first method accessing data would be preferred as this doesn't constrain the class to work with a specific type of struct but Xcode just throws the warning "Incomplete type 'void' is not assignable."
I don't want to have multiple methods for different types of data structures but I can't see any way to get around this currently. Having methods that return pointers to different types doesn't look very elegant and adds a lot of vestigial unused (and unwanted) code.
Is there a way to work around this to create an agnostic data store? I'd prefer for my mesh class to have different VertexAttributeArray objects to store vertices, indices, normals and colours etc so storing vector2, vector3 and vector4 is ideally where i'd like to be. How would I work around achieving this?

There is no way to do what you want. It's not known at compile-time what size the objects inside data is. ((void *)foo)[x] would be illegal in C as well. The compiler must know what struct is stored inside the array for you to be able to index it.
You can change the call to:
((CGRect *)[vertexAttributeArray data])[0] = CGRectMake(0.0, 0.0, 0.0, 0.0);
if you prefer, since then it's known that there is sizeof(CGRect) distance between the objects inside data.

Related

Singleton function not retaining value

I'll preface this question by saying that I am a total noob when it comes to Objective-C. So please, be patient with my question. :)
So here is my issue. I am basically allowing the user to 'rub out' an image by using alpha blending and such, and then converting the created texture to a CCSprite. I am then able to store the CCSprite in a function within my singleton class. Like so:
erasedTextureSprite = [CCSprite spriteWithTexture:darknessLayer.sprite.texture];
[[MySingleton sharedMySingleton] setRevealTexture:erasedTextureSprite];
Here is the setRevealTexture & getRevealTexture function as well as the revealTexture variable initialisation in my MySingleton.h file:
#interface MySingleton : NSObject
{
CCSprite *revealTexture;
}
...
-(void) setRevealTexture: (CCSprite *) texture;
-(CCSprite *) getRevealTexture;
And here are both functions in my MySingleton.m file:
-(void) setRevealTexture: (CCSprite *) texture
{
NSLog(#"set reveal texture.");
revealTexture = texture;
NSLog(#"%f", [revealTexture boundingBox].size.width);
}
-(CCSprite *) getRevealTexture
{
NSLog(#"got reveal texture.");
NSLog(#"%f", revealTexture.contentSize.width);
return revealTexture;
}
If I set the reveal texture, and then get it right away, it seems to return the sprite correctly. However, if I set the texture and then transition to another scene, it seems to lose it's value, and throws me an error when I try and call the getRevealTexture function.
Question: Why is my function not retaining it's value when I transition between scenes?
If any more clarification is needed please let me know!
Practically there is no point in using your own setter and getter if your not doing anything fancy.
What you are doing is using an iVar which should have been a strong property.
you could achieve the same by doing:
#property (nonatomic, strong) CCSprite *revealTexture;
in your header file.
You would then get the following 2 functions by default:
[[MySingleton sharedMySingleton] revealTexture];
[[MySingleton sharedMySingleton] setRevealTexture:texture];
My next point of failure that I would presume if this doesn't work is that your singleton function is not working properly and your actually getting a new instance each time.
You can easily find this out by doing in the debugger:
po [MySingleton sharedMySingleton]
And seeing the instance that is returned each time.
example for a basic singleton code:
static MySingleton *__sharedMySingleton = nil;
+ (id)sharedMySingleton {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__sharedMySingleton = [MySingleton new]; // or a specific alloc init function
});
return __sharedMySingleton;
}
Due to the request - here is a short image to explain how to enter debugger commands in xcode 5:

objective c large random method, am i doing this right?

I've been building a simpler game with core graphics with random scripted encounters if that makes any sense and its started to become a huge method in the view controller.
-(void)spawnStuff{
CGPoint position;
CGPoint position1;
int chance = random()%10;
switch (chance) {
case 0:
[self spawnWall];
Position.y = 580;
Position.x = 160;
wall.center = Position;
[self spawnWall];
Position1.y = 480;
Position1.x = 80;
wall.center = Position1;
}
-(void)spawnWall{
UIImage* myImage = [UIImage imageNamed:#"Wall.png"];
Wall = [[Sprite alloc] initWithImage:myImage];
CGRect rect = CGRectMake(0, 0, 90, 50);
more initilization stuff }
and I might repeat this line of code 20 - 30 different with positions of these walls and with only 10 different scenario its about a 3rd of the code in the class, its starting to get a lil redonkulous I'm still very new to programming and giant methods kinda scare me. Is this the right way to approach this?
If you do this kind of stuff often, I would suggest keeping an array of possible actions, and then just randomly pick one when necessary. Something like the following should show you how to do that.
Note, that you can add/subtract from the array at will, which changes the available blocks... This just shows, at the bottom, randomly picking and executing a block 100 times.
You can add/remove block from the appropriate places in your code to keep it local. This should at least get you started...
// If we want to perform some action randomly, then we build an array
// of possibly actions (blocks), and then randomly pick one and execute it.
// Figure out what type of block you want...
typedef void(^ActionBlock)(void);
NSMutableArray *possibleActions = [NSMutableArray array];
[possibleActions addObject:^{
NSLog(#"Doing Action Foo");
}];
[possibleActions addObject:^{
NSLog(#"Doing Action Bar");
}];
[possibleActions addObject:^{
NSLog(#"Doing Action Baz");
}];
[possibleActions addObject:^{
NSLog(#"Doing Action FooBar");
}];
[possibleActions addObject:^{
NSLog(#"Doing Action Blarg");
}];
[possibleActions addObject:^{
NSLog(#"Doing Action Zip");
}];
// Now, when I want to pick an action to perform...
for (int i = 0; i < 100; ++i) {
ActionBlock block = [possibleActions objectAtIndex:arc4random_uniform(possibleActions.count)];
block();
}
EDIT
The line
typedef void(^ActionBlock)(void);
is a typedef, and defines a type for a specific kind of block. Granted, the syntax for functions and blocks is a bit hard to read. Maybe I should take a step back. I hope it does not offend you, and apologize in advance if it is patronizing in any way. It is similar to
typedef int IntType;
typedef void const * OpaqueType;
which are a bit easier to read. The first defines a type, IntType of type integer, and the second defines a type, OpaqueType, of type "pointer-to-const-void." You can then declare variables like...
IntType someInteger;
OpaqueType someOpaqueValue;
Now, in basic C, we can also declare function pointers. Lets say we have some functions...
int functionOne(int arg1, double arg2) {
// do whatever...
}
int functionTwo(int arg1, double arg2) {
// do whatever...
}
You can call that function easily enough...
int result = functionOne(42, 3.14159);
Well, you can also create a variable that points to the function, and you can call it like any other function.
int(*function)(int,double) = functionOne;
int result = function(42, 3.14159); // Calls functionOne
function = functionTwo;
result = function(42, 3.14159); // calls functionTwo
However, that syntax is a bit wearisome to type. So, especially for functions and blocks, we create typedefs. To create a typedef for a function, it's identical to creating a function pointer variable, but you just put the type in the place of the variable name. So...
typedef int(*MyFunction)(int, double);
declares a type that represents a pointer to a function that returns an int, and takes two parameters, and int and a double.
Now, the syntax for blocks is just like for function pointers, except trading a ^ for a *. So, out type...
typedef void(^ActionBlock)(void);
means that ActionBlock is a type that represents a block that returns void and takes no parameters.
So, you can declare variables of type ActionBlock, and assign them to blocks that have void return and no arguments. Which, in the example, is exactly what I did.
Note, that in the code, we create a bunch of blocks like this...
[possibleActions addObject:^{
NSLog(#"Doing Action Foo");
}];
The compiler allows you to leave out stuff that is obvious, so that code really says... Create a block, that takes no arguments, and has return type void, and then put it into the add it to the collection.
We do this multiple times, and then, when ready to invoke the functions, we just iterate over the collection. however, the compiler wants to know how to call the function/block, and all it gets from the collection is an . So, we want to cast the object we get to a type that tells the compiler it is a block that returns void, and takes no arguments.
When we do that, we can then call the function/block. So, let's look at that last piece again (with some comments), and a little bit less compact
for (int i = 0; i < 100; ++i) {
// We have a bunch of blocks stored in the array.
// We want to pick one at random, so we will get a random number
// between [0, size of our array).
uint32_t index = arc4random_uniform(possibleActions.count);
// Now, we have a random index into our array. Get the object in that position.
id randomObject = [possibleActions objectAtIndex:index];
// We have an opaque id type, but we know it's a block. Cast it to a block type.
ActionBlock block = randomObject;
// Now, we have our block, in a type that the compiler will let us invoke.
// Call it like any other function.
block();
}
EDIT
OK, here you go. Create an iVar, NSTimer *_timer. Schedule it in didLoad, and invalidate it in didUnload and dealloc
- (void)dealloc {
[_timer invalidate];
}
typedef void(^ActionBlock)(void);
- (void)callRandomFunction:(NSTimer*)timer {
// Our userInfo is actually our array of blocks
NSArray *actions = timer.userInfo;
ActionBlock block = [actions objectAtIndex:arc4random_uniform(actions.count)];
block();
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *possibleActions = [NSMutableArray array];
// Create the list of possible functions that can be called...
// Now setup a timer that performs callRandomFunction: every second
// Pass the array of blocks as the userInfo of the timer.
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(callRandomFunction:) userInfo:possibleActions repeats:YES];
// Whatever else you need in here...
}
- (void)viewDidUnload
{
[_timer invalidate];
// Any other unload stuff...
[super viewDidUnload];
}

Getting very odd 'incompatible objective-c types' error. Maybe somebody else can see where i'm going wrong?

It must be one of those things where there's a tiny mistake i've missed, or something, but i can't seem to figure it out.
Viewcontroller.h
#import "RGBEditView.h"
#interface ColorPickerView : UIViewController {
RGBEditView *rgbEditView;
}
-(void)showRGBEditor;
.m
-(void)showRGBEditor {
rgbEditView = [[RGBEditView alloc] initWithFrame:CGRectMake(0, 0, 280, 46) H:h S:s B:b];
}
It's this line above, the initwithframe line, that gives the error 'Incompatible Objective-C types assigning '*', expected '*'
RGBEditView.h
#interface RGBEditView : UIView {
}
-(RGBEditView *)initWithFrame:(CGRect)frame H:(float)hue S:(float)saturation B:(float)brightness;
RGBEditView.m
-(RGBEditView *)initWithFrame:(CGRect)frame H:(float)hue S:(float)saturation B:(float)brightness {
[super initWithFrame:frame];
return self;
}
Can anybody see my problem? I'm very confused about this.
EDIT:
The problem lies in that I have another class which also uses initWithFrame:H:S:B:, so the only way to fix this is to change on of them to something a bit different, but this seems like an awkward work around. Any other solutions?
the methods init and methods that start with initWith should return type id.
what typically happens is that you have 2 classes with the same method name (initializer in this case), but differ in their return types:
RGBEditView
-(RGBEditView *)initWithFrame:(CGRect)frame H:(float)h S:(float)s B:(float)b;
HSBEditView
-(HSBEditView *)initWithFrame:(CGRect)frame H:(float)h S:(float)s B:(float)b;
alloc returns id - the compiler warns you because it sees an expression which resembles type assignment used in the following example:
RGBEditView * rgb = /* ... */;
HSBEditView * hsb = nil;
hsb = rgb // << compiler: "hey - you don't want to do that unless
// RGBEditView were a subclass of
// HSBEditView... but it's not!"
you correct this by returning id from your initializers, like this:
-(id)initWithFrame:(CGRect)frame H:(float)h S:(float)s B:(float)b;
you return id to avoid clashes like this, and because the compiler doesn't know what type is returned via alloc, so every subclass declaration would have to return a different type - which would only lead to more problems.
the exception to this is to use well qualified names - and is typically seen in convenience constructors:
+ (HSBEditView *)newHSBEditViewWithFrame:(CGRect)frame
H:(float)h S:(float)s B:(float)b;
in RGBEditView.m try
self = [super initWithFrame:frame];
return self;
initWithFrame should be
-(RGBEditView *)initWithFrame:(CGRect)frame H:(float)hue S:(float)saturation B:(float)brightness {
self = [super initWithFrame:frame];
return self;
}

Store enums in an NSArray?

I'm new to Objective-C, but experienced in C++ and C.
I want to store some enum constants in an array. In C++ I would do something like this:
enum color {RED, BLUE, YELLOW, GREEN};
vector<color> supportedColors;
supportedColors.push_back(RED);
supportedColors.push_back(GREEN);
But the NSArray will only store object pointers (id's). So how should they be stored? I could possibly cast them to integers and store them in an NSNumber object, but this seems messy.
I wonder what experienced obj-c programmers do?
Cast them to integers and store them in NSNumbers. :)
Native C types are really second class citizens in the Cocoa collection classes, and are often verbose to work with if you want to intermingle. C says that enums have integral values, so it's safe to use them as ints in this way.
Depending on what you're doing of course, you can simplify the manipulation code by wrapping that enum in an actual object ("MyColor") that has the enum as a property on it. These objects could then be tossed around in the collection classes as desired, with some upfront and runtime overhead that are unlikely to matter from a perf standpoint, depending on what you're doing.
You are perhaps looking for a way to simply loop through all of the options? How about just a plan old normal array?
typedef enum {RED,BLUE,GREEN,YELLOW} color;
color colors[4]={RED,YELLOW,GREEN,BLUE};
for (int i=0;i<4;i++)
colors[i];
On the other hand if performance isn't an issue and you are just looking to clean up the code a little; how about creating a class ColorArray that encapsulates NSMutableArray and creates the relevant methods.
ColorArray.h:
#import <Foundation/Foundation.h>
typedef enum {RED,BLUE,GREEN,YELLOW} Color;
#interface ColorArray : NSObject {
NSMutableArray* _array;
}
- (id) initWithArray:(Color[])colors;
- (void) addColor:(Color)color;
- (Color) colorAtIndex:(int)i;
#end
ColorArray.c:
#import "ColorArray.h"
#implementation ColorArray
- (id) init {
if (self = [super init]) {
_array = [[NSMutableArray alloc] init];
}
return self;
}
- (id) initWithArray:(Color[])colors {
if (self = [super init]) {
_array = [[NSMutableArray alloc] init];
for (int i=0;colors[i]!=0;i++)
[_array addObject:[NSNumber numberWithInt:colors[i]]];
}
return self;
}
- (void) dealloc {
[_array release];
[super dealloc];
}
- (void) addColor:(Color)color {
[_array addObject:[NSNumber numberWithInt:color]];
}
- (Color) colorAtIndex:(int)i {
return [[_array objectAtIndex:i] intValue];
}
#end
i would make a new class define the enum, and then instantiate supported colours as
vector<colorEnumClass> supportedColours
you can then do things like : give the class methods for testing a colour to see if it is a member of the vector .. then you can use that method in IF statements rather than explicit testing using relational operators. Clearer coding :-)
notice that the name supportedColours suggests that this vector should be a constant, defined at program start and never changed. If this is the case then the "colours" in the vector should be set in the constructor and never changed. The class should be implemented as a Singleton and you might even override the vector operators of pushback() etc to block modifications.
This is in the nature of the "new" Java enumeration technology
i should also mention that i am too new to Objective C to provide a syntactically correct code example .. sorry.

Simple layer problem [iphone]

Basically I'm sorting through objects in an array by y value (The furthest down the page is at the start of the array) but I'm having a bit of trouble. I assign all of the UIImageViews in an array a value:
for (UIImageView *Blocky in objectsArray){
[Blocky.layer setValue:[NSString stringWithFormat: #"%f",
Blocky.center.y] forKey:#"value"];
}
Because it's a UIImageView I have to put layer after "Blocky" otherwise I get the following error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[<UIImageView 0x3d44880>
setValue:forUndefinedKey:]: this class
is not key value coding-compliant for
the key value.'
My problem is when I'm sorting it I don't know where to put the ".layer" so I get the same problem because UIImageViews can't deal with keys on there own. Here is my sorting code:
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"value" ascending:YES];
[objectsArray sortUsingDescriptors:[NSArray
arrayWithObject:sortDescriptor]];
[sortDescriptor release];
Thanks in advance,
Ozzie
Use the NSMutableArray sortUsingFunction:context: method:
// top of file
static NSInteger compareImageHeights(id image1, id image2, void* context) {
CGFloat temp = ((UIImageView*)image2).center.y - ((UIImageView*)image1).center.y;
return (temp>0)?NSOrderedAscending:(temp<0)?NSOrderedDescending:NSOrderedSame;
}
// within file somewhere
[objectsArray sortUsingFunction:compareImageHeights context:NULL];
The reason the function is required (and you can't just use sortUsingSelector: or sortUsingSortDescriptor:) is that you can't specify a KVC key to identify just the y component only as a sort variable.
Edit: Changed center.y to size.height.
Edit: Oops. Changed size.height back to center.y. Fixed type to UIImageView rather than UIImage. It seems KVC objects can in theory support arbitrary keys, but some will and some won't.
Are you sure CALayer is supposed to accept arbitrary KVC keys? This looks wrong to me.
Shouldn't you extract the heights to a buffer of structures and qsort that?
eg
I think it would be something like this:
//
// before #implementation sections
//
typedef struct __MySortEntry {
UIImageView* image;
CGFloat height;
} MySortEntry, *PMySortEntry;
static int compareMySortEntry (const void * a, const void * b)
{
CGFloat temp =( ((PMySortEntry)a)->height - ((PMySortEntry)b)->height );
return (temp<0)?-1:(temp>0)?1:0;
}
//
// in #implementation section somewhere
//
NSMutableData* mutable = [[NSMutableData alloc] initWithLength:
sizeof(MySortEntry)*objectsArray.count];
PMySortEntry entries = (PMySortEntry)mutable.mutableBytes;
for (int c = 0; c < objectsArray.count; c++) {
entries[c].image = (UIImageView*)[objectsArray objectAtIndex:c];
entries[c].height = entries[c].image.center.y;
}
qsort(entries, objectArray.count, sizeof(MySortEntry), compareMySortEntry);
for (int c=0; c < objectArray.count; c++) {
UIImage* imageInSequence = entries[c].image;
// do something with images **in sequence**
}
[mutable release];
Edit: Changed center.y to size.height.
Edit: Changed size.height back to center.y. Oops.
Edit: Changed UIImage to UIImageView.
Why not set the tag property on each view as you create them based on the height and retrieve them by tag?