i have this code and on the second line it gives me an error that says invalid initialzer
this is the code:
-(void)setPlayerPosition:(CGPoint)position {
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collision = [properties valueForKey:#"Collidable"];
if (collision && [collision compare:#"True"] == NSOrderedSame) {
return;
}
}
}
_player.position = position;
}
I have the same problem with you. But when I declare function [tileCoordForPosition] in .h file, everything is built success.
I'm going to use my psychic powers and guess that you accidentally declared tileCoordForPosition: as returning a CGPoint* (that is, a pointer to a CGPoint) rather than a CGPoint.
(It would, however, have been helpful if you'd shown the relevant code so I wouldn't have to fire up the ESP.)
Related
I have a controller that is registered as an observer for a LOT of properties on views. This is our -observeValueForKeyPath:::: method:
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void*)context
{
if( context == kStrokeColorWellChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kStrokeColorProperty];
}
else if( context == kFillColorWellChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kFillColorProperty];
}
else if( context == kBodyStyleNumChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kBodyStyleNumProperty];
}
else if( context == kStyleChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kStyleProperty];
}
else if( context == kStepStyleChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kStepStyleProperty];
}
else if( context == kFirstHeadStyleChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kFirstHeadStyleProperty];
}
else if( context == kSecondHeadStyleChangedContext )
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kSecondHeadStyleProperty];
}
And there's actually about 3x more of these else if statements.
One thing you can see is that each block has the same code, which makes me think that it's possible to optimize this.
My initial thought was to have an NSDictionary called keyPathForContextDictionary where the keys are the constants with the Context suffix (of type void*), and the values are the appropriate string constants, denoted by the Property suffix
Then this method would only need one line:
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:keyPathForContextDictionary[context]];
Note that I need to use a data structure of some sort to identify which keyPath to use, and I can't simply use the keyPath argument passed into the method. This is because there are multiple views that have the same property I'm observing (for example, color wells have the color property). So each view needs to determine a unique keypath, which is currently being determined based off of the context
The problem with this is that you cannot use void* as keys in an NSDictionary. So... does anybody have any recommendations for what I could do here?
EDIT:
Here's an example of how the constants are defined:
void * const kStrokeColorWellChangedContext = (void*)&kStrokeColorWellChangedContext;
void * const kFillColorWellChangedContext = (void*)&kFillColorWellChangedContext;
void * const kBodyStyleNumChangedContext = (void*)&kBodyStyleNumChangedContext;
void * const kStyleChangedContext = (void*)&kStyleChangedContext;
NSString *const kStrokeColorProperty = #"strokeColor";
NSString *const kFillColorProperty = #"fillColor";
NSString *const kShadowProperty = #"shadow";
NSString *const kBodyStyleNumProperty = #"bodyStyleNum";
NSString *const kStyleProperty = #"style";
The type void * is not so much a type unto itself that you have to match, as it is "generic pointer". It's used for the context argument precisely so that you can use any underlying type that you like, including an object type. All you have to do is perform the proper casts.
You can therefore change your kTHINGYChangedContexts to be NSStrings or any other object you like very easily, and then use them as keys in your context->key path mapping.
Start with:
NSString * const kStrokeColorWellChangedContext = #"StrokeColorWellChangedContext";
When you register for observation, you must perform a bridged cast:
[colorWell addObserver:self
forKeyPath:keyPath
options:options
context:(__bridge void *)kStrokeColorWellChangedContext];
Then when the observation occurs, you do the reverse cast:
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void*)ctx
{
NSString * context = (__bridge NSString *)ctx;
// Use context, not ctx, from here on.
}
And proceed to your key path lookup from there.
Josh Caswell had a great answer, but I didn't want to modify the type of our constants into NSStrings*
So a solution instead, was to cast the void* into NSValues w/ -valueWithPointer. This way I could use the void* as keys in my dictionary
Here's the code:
NSString *toolKeyPath = [[ToolController keyPathFromContextDictionary] objectForKey:[NSValue valueWithPointer:context]];
if( toolKeyPath )
{
if( [change objectForKey:NSKeyValueChangeNewKey] == (id)[NSNull null] )
{
[self setValue:nil forKey:toolKeyPath];
}
else
{
[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:toolKeyPath];
}
}
And the dictionary:
+(NSDictionary*) keyPathFromContextDictionary
{
return #{
[NSValue valueWithPointer:kStrokeColorWellChangedContext] : kStrokeColorProperty,
[NSValue valueWithPointer:kFillColorWellChangedContext] : kFillColorProperty,
[NSValue valueWithPointer:kBodyStyleNumChangedContext] : kBodyStyleNumProperty,
[NSValue valueWithPointer:kStyleChangedContext] : kStyleProperty,
[NSValue valueWithPointer:kStepStyleChangedContext] : kStepStyleProperty,
[NSValue valueWithPointer:kFirstHeadStyleChangedContext] : kFirstHeadStyleProperty,
[NSValue valueWithPointer:kSecondHeadStyleChangedContext] : kSecondHeadStyleProperty,
[NSValue valueWithPointer:kShadowChangedContext] : kShadowProperty,
[NSValue valueWithPointer:kStrokeWidthChangedContext] : kStrokeWidthProperty,
[NSValue valueWithPointer:kBlurRadiusChangedContext] : kBlurRadiusProperty,
[NSValue valueWithPointer:kFontSizeChangedContext] : kFontSizeProperty
};
}
I am trying to implement a tricky thing: all my model classes have an automatic NSCoding implementation of their properties. This let me add and remove properties to my classes and do not worry about missing stuff being not encoded/decoded. (Actually, it serves also other purposes, but that's the main idea).
When initializing my object from archive with initWithCoder, thanks to the Obj-C runtime, I go through the list of my properties, and try to assign directly the ivars to the values. I do not want to go through setters for various and imperious reasons, hence setValue:forKey: is forbidden.
Interesting problem, isn't it? I must say I am not full confident with C-pointers subtleties...
Here is the code:
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(class, &outCount);
for (unsigned int index = 0; index < outCount; index++) {
objc_property_t property = properties[index];
NSString *propertyNameString = [NSString stringWithUTF8String:property_getName(property)];
id value = [coder decodeObjectForKey:propertyNameString];
if (value == nil) {
continue;
}
const char *attributes = property_getAttributes(property);
NSString *typeAttribute = [[NSString stringWithUTF8String:attributes] substringWithRange:NSMakeRange(1, 1)];
const char *ivarName = [[#"_" stringByAppendingString:propertyNameString] UTF8String];
if ([typeAttribute isEqualToString:#"#"]) {
Ivar ivar = class_getInstanceVariable([self class], ivarName);
object_setIvar(self, ivar, value);
}
else if ([typeAttribute isEqualToString:#"d"]) {
double *doublePointer = getIvarPointer(self, ivarName);
*doublePointer = [value doubleValue];
}
else if ([typeAttribute isEqualToString:#"i"]) {
int *intPointer = getIvarPointer(self, ivarName);
*intPointer = [value intValue];
}
else if ([typeAttribute isEqualToString:#"c"]) {
char *charPointer = getIvarPointer(self, ivarName);
*charPointer = [value boolValue];
}
else if ([typeAttribute isEqualToString:#"Q"]) {
NSUInteger *uintegerPointer = getIvarPointer(self, ivarName);
*uintegerPointer = [value unsignedIntegerValue];
}
}
free(properties);
The 'getIvar' function looks like this (yes, I use class_getInstanceVariable on object class, since object_getInstanceVariable is not allowed with ARC...):
static void* getIvarPointer(id object, char const *name)
{
Ivar ivar = class_getInstanceVariable(object_getClass(object), name);
if (!ivar) return 0;
return (unsigned char *)(__bridge void *)object + ivar_getOffset(ivar);
}
When running Xcode's static analyzer, I get a warning saying "Dereference of null pointer (loaded from variable 'doublePointer')". Interestingly enough, a similar message appeared for int and NSUInteger, but seems to have disappear right now... It never appeared for 'char'.
Any idea, suggestion or insightful criticism would be very much appreciated.
I must say that, the code actually works. I do get automatic decoding of double and int in my object classes. But I want to understand why the static analyzer tells me such thing.
As your getIvarPointer() function can legibly return 0, you must handle this eventuality in each *ptr affectation I think.
For example:
if ([typeAttribute isEqualToString:#"d"]) {
double *doublePointer = getIvarPointer(self, ivarName);
if (doublePointer) {
*doublePointer = [value doubleValue];
} else {
// Handle this as you can ;)
}
}
Hey I have a bit of code that keeps returning an error message, but i can't figure out what the problem is. I am fairly new to Objective-c syntax so it could be something fairly simple, however i can not seem to find the problem. The method is
-(void)setPlayerPosition:(CGPoint) position {
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [metaLayer tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collision = [properties valueForKey:#"Collidable"];
if (collision && [collision compare:#"True"] == NSOrderedSame) {
return;
}
NSString *collectable = [properties valueForKey:#"Collectable"];
if (collectable && [collectable compare:#"True"] == NSOrderedSame) {
[metaLayer removeTileAt:tileCoord];
[foreground removeTileAt:tileCoord];
}
}
}
player.position = position;
}
It returns the error Invalid initializer for the 2nd line and also says that my class may not respond to 'tileCoordForPosition:' Any help would be appreciated!
I assume you are following the article at http://www.raywenderlich.com/1186/collisions-and-collectables-how-to-make-a-tile-based-game-with-cocos2d-part-2
So after briefly reading it myself, my guess is you are either missing the method required or are not declaring it properly in your class.
You should have the following method declared and implemented
- (CGPoint)tileCoordForPosition:(CGPoint)position
And it should be in the same class as where you call it on self
[self tileCoordForPosition:position]
If you do have that method implemented in your class then make sure you are declaring it correctly too. Try adding it to the top of your .m file like so
#interface YourControllerNameHere()
- (CGPoint)tileCoordForPosition:(CGPoint)position;
#end
You need to declare tileCoordForPosition: in your header so the compiler knows what type it returns (in this case, a CGPoint).
Strangest thing I've seen yet.. NSLog is failing within a method based on something bizarre. Here is the code:
-(void) testBoard {
BoardManager* bm = [BoardManager manager];
Board* board = [bm boardForName:#"fourteen by eight"];
NSLog(#"%#", board);
NSLog(#"%#", board.coords);
}
in Board.m :
-(NSArray*) coords {
if(!_coords.count && _definition) {
NSArray* c = [Board decode:_definition];
[self setCoords:c];
}
return _coords;
}
+(NSArray*) decode:(NSString*)encodedCoords {
NSMutableArray* coords = [NSMutableArray array];
NSArray* tokens = [encodedCoords componentsSeparatedByString:#","];
int i = 0;
NSString* dimStr = [tokens objectAtIndex:i++];
int width = [dimStr substringToIndex:2].intValue;
int height = [dimStr substringWithRange:NSMakeRange(2, 2)].intValue;
int depth = [dimStr substringFromIndex:4].intValue;
NSLog(#"w=%d h=%d d=%d", width, height, depth);
NSString* b128;
NSString* b2;
for(int z=0; z<depth; z++) {
for(int y=0; y<height; y++) {
b128 = [tokens objectAtIndex:i++];
NSLog(#"[%#]", b128);
b2 = [Board base128to2:b128];
NSLog(#"b2=%#",b2);
for(int x=0; x<b2.length; x++) {
if([b2 characterAtIndex:b2.length-1-x] == '1') {
Coord* coord = [Coord x:width-1-x y:height-1-y z:z];
[coords addObject:coord];
}
}
}
}
return coords;
}
Now what happens is, none of the NSLog statements within decode: or methods called from decode: will log to the console. However, NSLog before and after calling decode: work, and the rest of the code around the NSLog's executes fine. I found that I could get all the NSLog statements to work simply by commenting out [coords addObject:coord];. This statement appears after NSLog statements that it is affecting. I also found I could get the NSLog's to work by rearranging a few lines in testBoard: like this:
-(void) testBoard
{
BoardManager* bm = [BoardManager manager];
Board* board = [bm boardForName:#"fourteen by eight"];
NSLog(#"%#", board);
NSString* def = board.definition;
NSArray* coords = [Board decode:def];
NSLog(#"%#", coords);
}
This seems quite bizarre..an xcode Loch Ness monster surfacing?!
Have you tried running in the debugger? What happens when you get to that if statement
if(!_coords.count && _definition)
Are you sure _coords.count is 0 and _definition is not nil
In my experience, NSLog macro expansions can fail. Most of the time it's obvious why, sometimes not.
Just do something like this:
id objToLog = /* whatever */;
NSLog(#"%#", objToLog);
If this approach works, you can go ahead with your development and maybe you figure the problem out later.
I'm getting used to OCMock. Coming from a Java/JMock background I'm now looking for the ability to say [[[myMock stub] returnValueFromCustomMethod] someMockedMethod]; where returnValueFromCustomMethod is defined in the test class. I was originally thinking something along the terms of [[[myMock stub] usingSelector:#selector(myMethod:)] someMockedMethod]; but after writing I wonder if my first approach makes more sense. Either way, could someone show me if and how this can be done?
My original answer was off-track: OCMock doesn't support this! If you wanted to change OCMock to support this, you would need to do something like adding a BOOL returnValueIsFromInvocation field to OCMockRecorder, and add a method to set this up:
- (id)andReturnResultOfInvocation:(NSInvocation *)anInvocation {
returnValueIsFromInvocation = YES;
returnValueIsBoxed = NO;
returnValueShouldBeThrown = NO;
[returnValue autorelease];
returnValue = [anInvocation retain];
return self;
}
Then teach setUpReturnValue to call the invocation (changes are in bold):
- (void)setUpReturnValue:(NSInvocation *)anInvocation
{
if (returnValueIsFromInvocation) {
NSInvocation *returnValueInvocation = (NSInvocation *)returnValue;
[returnValueInvocation invoke];
void *buffer = malloc([[anInvocation methodSignature] methodReturnLength]);
[returnValueInvocation getValue:buffer];
[anInvocation setReturnValue:buffer];
free(buffer);
}
else if(returnValueShouldBeThrown)
{
#throw returnValue;
}
else if(returnValueIsBoxed)
{
if(strcmp([[anInvocation methodSignature] methodReturnType],
[(NSValue *)returnValue objCType]) != 0)
[NSException raise:NSInvalidArgumentException
format:#"Return value does not match method signature."];
void *buffer = malloc([[anInvocation methodSignature] methodReturnLength]);
[returnValue getValue:buffer];
[anInvocation setReturnValue:buffer];
free(buffer);
}
else
{
const char *returnType = [[anInvocation methodSignature] methodReturnType];
const char *returnTypeWithoutQualifiers = returnType + (strlen(returnType) - 1);
if(strcmp(returnTypeWithoutQualifiers, #encode(id)) == 0)
[anInvocation setReturnValue:&returnValue];
}
}
This change is difficult to do by introducing subclasses because you have to override the methods that return OCMockRecorders (like stub, expect and so on) but the concrete subclasses of OCMockObject (OCClassMockObject and OCProtocolMockObject) are hidden by the framework.