Okay, just for starters, I am very new to Objective-C, (C in general). I am not new to programming, and I've found the transition seamless so far, until now. I'm trying to implement a Sprite class I found online in order to develop a game on the iPhone, but I'm getting many errors. For example...
size = CGSizeMake([image size].width , [image size].height);
Where size is a CGSize object, synthesized and image is a UIImage object, synthesized. They are both declared in the Sprite.h file. The error I'm getting is "incompatible types in this assignment". Any help would be greatly appreciated. I feel as though the error is something simple, like an #include or something because every error has to do with a CoreGraphics function (CGRectMake, CGSizeMake, CGRectIntersectRect, etc.). I didn't know if I should post the entire class here, but I'll be happy to if requested to.
When you say "size" is a CGSize object, I think you have a misunderstanding. CGSize is a regular C structure, and CGSizeMake returns a "CGSize" not a "CGSize *". My guess is that "size" is declared as "CGSize *" because you thought it was an object. Try removing the *.
Related
I was porting some ObjectiveC custom UIView subclasses to Swift this morning. Wanting to make it more "object oriented", I was following the example of extending CGContext with methods. E.g.
extension CGContext {
func fillColor(color:UIColor) {
CGContextSetFillColorWithColor(self, color.CGColor)
}
}
Because I was converting the objective C style messages (e.g. -(void) drawOutline: (CGContextRef) cr {...}) to Swift style ones without paying to much attention (e.g. func drawOutline(cr:CGContextRef) {...}), I didn't realize at first that I was passing CGContextRef arguments, but had extended CGContext (not Ref). But it worked!
What was weirder, was when I changed those CGContextRef's to just CGContext's. And it still worked. Not just compiled, but ran and drew correctly. For the fun of it, I changed to extension CGContextRef. And yet it still continues to work. It's as if Swift is smart enough to boil them down to the same thing. Is it? Or is there something more subtle/cool going on here?
There's nothing particularly magical here. CGContextRef and CGContext are just different names for (almost) the same thing. Swift just blurs the typedef for you.
<CoreGraphics/CGContext.h>
typedef struct CGContext *CGContextRef;
See also the Using Swift with Cocoa and Objective-C guide section on Core Foundation:
When Swift imports Core Foundation types, the compiler remaps the names of these types. The compiler removes Ref from the end of each type name because all Swift classes are reference types, therefore the suffix is redundant.
They just chose not to take away the Ref form of the name, I guess for old timers who are used to typing it :D
I am trying to write an iPhone game using cocos2d and Box2D, which means I have to mix between Objective-C and C++. I have a question regarding using pointers to objects of gameLevels and levelObjects that I have created.
I have a class called GameLevel which contains a vector of levelObjects. I thought that using pointers was the way to do it, to make sure I use as little memory as possible. That means that my vector is
std::vector<LevelObject*>* levelObjects;
The LevelObject class contains the sprite, b2bodyDef and so on, all pointers as well.
The problems started when I wanted to iterate through this vector when I wanted to draw a level. The code I wrote was this:
-(void)startLevel:(b2World*) world withLevel:(GameLevel*) level{
std::vector<LevelObject*>::iterator it;
for (it = level->getLevelObjects()->begin() ; it != level->getLevelObjects()->end(); ++it) {
CCSprite* sprite = it->sprite; //Here XCode complaints about "Member reference base type 'LevelObject *' is not a structure or union" and "Use of undeclared identifier 'sprite'". I cannot seem to access any of the objects variables/methods from it->
So the question I ask is: Is this a good way to to this, with a pointer to vector of pointers? And why doesn't the iteration work?
Thanks :)
EDIT: getLevelObjects() are:
std::vector<LevelObject*>* getLevelObjects(){return levelObjects;}
Your iterators point to pointers, so you need two dereferences:
(*it)->sprite
the dereference of the iterator,
*it
gives you a LevelObject*.
You have two levels of indirection, the iterator and the pointer inside the vector.
You need (*it) to get to the pointer, and then use -> to dereference that:
CCSprite* sprite = (*it)->sprite;
I saw someone passing arguments by a button's tag as follow.
how could this work? and is it safe to do like this ?
NSCustomClass* cls = [[NSCustomClass alloc] init:#"",#"",#""];
[button setTag:(int) cls];
- (void)OnClick:(id)sender
{
NSCustomClass* cls = (NSCustomClass*)[sender tag];
// to do something with the "cls".
[cls release];
}
In fact,I didn't get weird results.
It works fine by passing arguments like 'NSString','NSArray' in this way.
Can you tell me why it is a bad idea?
Casting a custom class to an int is a very bad idea and you'll get weird results.
The tag is an integer property on all UI elements. It is declared as such on UIView:
#property(nonatomic) NSInteger tag;
You can assign any integer value to it, including any predefined constants:
#define Button1Constant 1
#define PushButtonConstant 2
// …Code
[button setTag:PushButtonConstant];
// …More code
if (sender.tag == PushButtonContent)
// Handle
In general you never want to abuse the frameworks. The tag is intended to store an integer and is used mainly to access a view with viewWithTag:, which can be useful in some cases if your view was built in Interface Builder but a referencing IBOutlet is inappropriate. Stuffing a pointer into an int can give unpredictable results, as others have indicated.
On top of that there's a memory management issue. Even if the pointer survives the cast you aren't retaining the object being pointed to. This in effect is a weak pointer but without any management by the run-time. Attempts to use the pointer will likely lead to the dreaded EXC_BAD_ACCESS. And who knows what ARC might do with this mess - blow up, I would expect.
To attach data to a button (which in and of itself sounds like a possible design flaw) you should either subclass or leverage the power of the run-time with objc_setAssociatedObject() and friends.
In general, casting from a pointer type to an integer type usually indicates a design flaw; there are very few legitimate reasons to do this, and I would strongly discourage doing it, especially in this case. It may work, but it's not guaranteed to work, simply because the tag property is not documented to be used this way. The more “hacks” that you put in your code, the harder it is to maintain.
Don't learn from the code where you saw this, instead stick to reputable tutorials and books.
As a side note, it is conventional in Objective-C to name all methods starting with a lowercase letter, so the method should be onClick: and not OnClick:. The exception to this rule is when the method starts with an acronym, for example -[NSString UTF8String].
You could subclass from UIButton and define a property by yourself, instead of using its tag property.
I'm working on an existing, large-ish codebase, and after upgrading the iOS SDK to 4.1 I am now seeing very strange behaviour. The crux of the matter appears to be a particular class that will no longer alloc - it is throwing a bad access in obj_msgSend, and seems to be the Class object on the stack that objc_msgSend doesn't like - although it is not actually NULL.
The original failing line looked like this:-
tileProjection = [[RMFractalTileProjection alloc] initFromProjection:proj tileSideLength:sideLength maxZoom:18];
I deconstructed this to isolate the problem:-
RMFractalTileProjection *p = [RMFractalTileProjection alloc]; // <- this crashes
p = [p initFromProjection:proj tileSideLength:sideLength maxZoom:18];
tileProjection = p;
I then tried this:-
Class c = NSClassFromString(#"RMFractalTileProjection");
assert(c);
NSLog( #"RMFractalTileProjection class(ptr) %p", c ); // <- prints an address OK
NSLog( #"RMFractalTileProjection class(obj) %#", c ); // <- crashes
In the debugger it looks like the Class object is sensible, but NSLog crashes when it tries to print it.
One thing to note: the class in question is declared as below, and I'm not sure if the protocol is causing a problem. Because this particular part is a large chunk of open source code, it is very difficult to remove this protocol requirement to see if that makes a difference.
#interface RMFractalTileProjection : NSObject<RMMercatorToTileProjection>
{
...
}
Any help on this one greatly appreciated - it is a show stopper.
Thanks
This is not really an answer but some ideas to move forward.
The only causes that leap to mind at the moment are memory corruption and some sort of link issue. Perhaps you are linking two versions of the class somehow.
Assuming this is the class, there doesn't look to be anything wrong to make it crash in alloc. There's no +initialize or anything.
Questions I would be asking myself and trying to answer are:
what happens if I rename the class?
what happens if I create a new identical class with a different name?
the pointer that gets passed to obj_msgSend: is it reasonable? does it point to something that looks like a class?
do you ever subclass the class and do you use initialize on the subclass?
is the pointer always the same? If so you can watch what it points to and see if it changes during execution.
what happens if you send self to the class?
OK, finally found this. As Jeremy suggested, this turned out to be a regular memory stomper.
The difficulty I had finding it was that it wasn't the Class object itself that was getting stomped, but the class' metaclass structure - which is a normal Class object but one level up, referenced by the class 'isa' pointer. That's why the class looked OK to me when I inspected it in the debugger - I need to follow the isa pointer and dump memory at one level up to find this. Luckily for me, the class was only a subclass of NSObject - had it been deeply subclassed, this could have been much harder to find. I got my first clue after biting the bullet, reverse-engineering objc_msgSend, working out exactly what was on the stack frame, and following all the pointers. Yep, the hard way :)
Matt Gallaghar's post (and various others I found by following links) were invaluable in helping me through this maze - thanks guys!
Burned a lot of time on this one, but on the up side I learned a hell of a lot about Objective C internals during the past day and a half :)
Thanks for these suggestions JeremyP - it is always good to have fresh suggestions after you've been banging your head against the keyboard all day!
Your suggestion of creating an identical class with the same name appears to have fixed the problem. I have no idea why and I feel I need to understand what's going on here. You're right it sounds like some kind of linker issue, but I still have no idea what could cause such a serious runtime error and not even produce a warning at build time.
Re. the pointer, it does look reasonable, but something inside the class eventually gets dereferenced as a null pointer inside objc_msgSend. Occasionally, after I have changed the code and rebuilt, I get a null pointer instead. This behaviour obviously suggests something nondeterministic like a memory stomp.
I'll post my findings.
My app involves a user being able to drag specific items such as volume sliders and UIImage views. My goal is to be able to save the locations of the items so that the user can not have to drag the items again after re-launch. Unfortunately, I have seen errors along taking the 'saving CGRect rect = item.frame to NSuserDefaultsorNSKeyedArchiver' route. Any other suggestions?
You could use NSStringFromgCGRect() and CGRectFromString() to store them.
If you're using keyed archiving on iPhone, UIKit specifically supports an addition that looks like this: -encodeCGRect:forKey: and does precisely what you want. See the Apple docs for NSCoder's UIKit additions.
If you're using NSUserDefaults, you don't get the luxury of using an explicit encoder for CGRects, but as some other answers say, you could save them as strings using NSStringFromCGRect() and its reverse.
You would probably encode CGRect using NSData and then store NSData object using NSUserDefaults or NSKeyedArchiver.
I'm not sure what errors exactly are you referring in your question, maybe more explanation needed?
You have to convert the floats in the CGRect to NSNumbers and then put them in a Dictionary.
If you need to store a lot of CGRects, I recommend creating a little lightweight class with the CGRect elements as archivable NSNumber attributes. Create an initializer that takes a CGRect. Then write quick NSCoder protocol methods so the entire object can be archived
That way you can you quickly and easy convert, store and then retrieve the CGRects.