Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

BadWolf13

macrumors 6502
Original poster
Dec 17, 2009
271
0
Ok, just came across this thing called "casting" in the Obj-C book I'm reading. They show a line of code that says;

Code:
[(NSButton *)sender setHidden:YES];

Now I tried to find more information about this casting, but I can't. I can't find a good description of it in the Objective-C Programming Guide, I can't find it anywhere else in this book. Does anyone know a good link for learning about this thing?
 
Casting for objects just basically silences compiler warnings.

If you are really curious as to what the type of an incoming object is, you should use methods like -(BOOL)respondsToSelector:(SEL)sel and -(BOOL)isKindOfClass:(Class)isClass

EDIT: Oh and it is also sometimes used to be more specific towards your intentions. Like saying, "this id sender is really an NSButton, so if that changes in the view, this viewController needs to be updated"
 
Is it worth using this to silence the compiler warnings? That was one of my reasons for being interested.

Also, does using a cast, like that example, permanently change the class of the pointer, or is it the effect only for that line of code?
 
Is it worth using this to silence the compiler warnings? That was one of my reasons for being interested.

Also, does using a cast, like that example, permanently change the class of the pointer, or is it the effect only for that line of code?

Let me preface this with saying that such a cast does not change anything about the actual object.

The effect of the cast is the same scope of the cast (to the best of my knowledge)

Is it worth it? You can write fully functional programs and have tons and tons of compiler warnings. If you cast or otherwise silence them all, when something goes awry it can be easier to find. Being more verbose is almost always a good thing.

If you have an object you think is an NSButton and you cast it as such, it will still generate a runtime error if you try to call methods that an NSButton responds to but this mystery object does not. And even right after the cast, if you ask it [somObj isKindOfClass: [NSButton class]]; will return false if it isn't an NSButton (or a subclass of NSButton). The cast doesn't change the object in any way.
 
Ok, I think I got it. I was thinking of using it on the following code;

Code:
	for (NSView *view in fields){
		if ([view isKindOfClass:[NSControl class]]) {
			[view setEnabled:edit];
		}		
	}

As it is, the code works perfectly, but gives me that annoying compiler warning.
 
You should always fix warnings. Do this:
Code:
[(NSControl *)view setEnabled:edit];
 
Kainjow is correct if to silence the warning.

The next question is intent/expectation.

Obviously you think some, of those views are going to be NSControls (or subclasses). Which means you could either remove the if statement, OR better yet make it

for (NSControl *control in fields) ...


If only SOME of them are NSControls then everything looks good here.
 
Ok, just came across this thing called "casting" in the Obj-C book I'm reading. They show a line of code that says;

Code:
[(NSButton *)sender setHidden:YES];

Now I tried to find more information about this casting, but I can't. I can't find a good description of it in the Objective-C Programming Guide, I can't find it anywhere else in this book. Does anyone know a good link for learning about this thing?

It is a good old C language cast. That's why I think people should learn C before Objective C, because otherwise they don't understand the basic things.

(NSButton*)sender doesn't cast the object, it casts the pointer to the object. "sender" is declared to have type "id", which is just a shortcut for "could be any Cocoa object". If you send a message to an object of type "id", like "setHidden:", the compiler has no idea what kind of object this is, so it cannot warn you if you send a message that the object doesn't understand. At runtime, your program will crash if it is an object that doesn't understand "setHidden:".

By casting sender to "NSButton*" you tell the compiler "I am absolutely sure it is an NSButton or a subclass of NSButton". Now the compiler can warn you if you send a message that an NSButton doesn't understand. A trivial case would be [(NSButton*)sender setHidden] vs. [sender setHidden]. There is no method setHidden without a parameter. When you send it to sender of type id, the compiler won't warn you because it has no idea whether sender could be an object that understands the message or not. When you send it to (NSButton*)sender the compiler can warn you, because it knows an NSButton* doesn't understand that method.


You should always fix warnings. Do this:
Code:
[(NSControl *)view setEnabled:edit];

Just saying: You should not blindly fix the warning with the goal of making the warning go away; you should check it, understand it, and then do the appropriate thing. In this case, yes, you have just checked that view is a control, so it is safe to cast the pointer to "NSControl*. Warning means that the compiler says "this looks like it might be wrong". So you make sure whether it is wrong or not first and either fix what is wrong, or do things to make the compiler not warn you.

The reason why all warnings should be gone is that then you can turn on "Warnings = errors" in the compiler, and the compiler will stop you when you run into a warning that actually _is_ a bug.
 
Last edited:
It is a good old C language cast. That's why I think people should learn C before Objective C, because otherwise they don't understand the basic things.
I 100% agree. Some people on these forums (more in the iPhone Programming side) suggest otherwise. If people don't know what a cast is, how pointers work etc they invariably get into trouble later.

The reason why all warnings should be gone is that then you can turn on "Warnings = errors" in the compiler, and the compiler will stop you when you run into a warning that actually _is_ a bug.
Pretty much everyone should turn that on. It will catch enough errors to make it worth the slight hassle.
 
I 100% agree. Some people on these forums (more in the iPhone Programming side) suggest otherwise. If people don't know what a cast is, how pointers work etc they invariably get into trouble later.


Pretty much everyone should turn that on. It will catch enough errors to make it worth the slight hassle.

I have an ongoing vendetta against -Werror (a lot of it for reasons I can't discuss, unfortunately). My suggestion would be to apply some measure of self discipline and a) minimize the number of warnings just as you would with -Werror, b) *actually read and understand warnings*.

-Werror will force you to fix them, but it does not force you to understand them, and it does not let you choose the time to fix them. For example: when a new OS is released, would you prefer to be able to test building your project on the new system immediately, or have to fix 10s or 100s of deprecation warnings by rewriting a bunch of code first? I would argue that testing building on a new OS is enough of a variable to introduce at one time, and rewriting code while doing that is just asking for questions like "huh... is this due to the new toolchain and libraries or is this due to my changes just now?".
 
For example: when a new OS is released, would you prefer to be able to test building your project on the new system immediately, or have to fix 10s or 100s of deprecation warnings by rewriting a bunch of code first?

If you set your target SDK back to the version prior to the one you are testing on does this not remove the deprecation warnings (at the cost of preventing you using any new features, but they we are meant to be doing one thing at once so that's not a worry)?
 
If you set your target SDK back to the version prior to the one you are testing on does this not remove the deprecation warnings (at the cost of preventing you using any new features, but they we are meant to be doing one thing at once so that's not a worry)?

It does, but I believe that counts as linking on the earlier OS in terms of triggering workarounds inside Cocoa (i.e. if (linked on or after <os version>) { do old wrong behavior people relied on }).

Could be misinterpreting how the target SDK thing works though.
 
Are there any cases where you would want to cast an object pointer for reasons other than silencing a warning? Would a scenario ever exist where you have an inherited object that overrides a method of its parent, but you want to call the parent's method? Like if B inherits from A and overrides method doSomething(), would you ever want to cast "B *" to "A *" to invoke A's doSomething()?
 
I have an ongoing vendetta against -Werror (a lot of it for reasons I can't discuss, unfortunately). My suggestion would be to apply some measure of self discipline and a) minimize the number of warnings just as you would with -Werror, b) *actually read and understand warnings*.

-Werror will force you to fix them, but it does not force you to understand them, and it does not let you choose the time to fix them. For example: when a new OS is released, would you prefer to be able to test building your project on the new system immediately, or have to fix 10s or 100s of deprecation warnings by rewriting a bunch of code first? I would argue that testing building on a new OS is enough of a variable to introduce at one time, and rewriting code while doing that is just asking for questions like "huh... is this due to the new toolchain and libraries or is this due to my changes just now?".

You can turn off warnings for deprecated methods and functions. Of course when you install the first 10.7 SDK, you shouldn't be using anything that is deprecated in 10.6 already (especially because it might be gone in 10.7).


Are there any cases where you would want to cast an object pointer for reasons other than silencing a warning? Would a scenario ever exist where you have an inherited object that overrides a method of its parent, but you want to call the parent's method? Like if B inherits from A and overrides method doSomething(), would you ever want to cast "B *" to "A *" to invoke A's doSomething()?

You can't in Objective-C. A method always invokes the implementation of the object, no matter what you tell the compiler. The only case where something different happens is when you call [super someMethod] which will call the implementation of the superclass of the implementation that you are currently executing. In C++, no cast is needed, you can just add the classname to the method name.
 
Last edited:
You can't in Objective-C. A method always invokes the implementation of the object, no matter what you tell the compiler. The only case where something different happens is when you call [super someMethod] which will call the implementation of the superclass of the implementation that you are currently executing. In C++, no cast is needed, you can just add the classname to the method name.

And [super someMethod] means that it will run "someMethod" in the current class' superclass, right? There is no way to do something like "[B.super someMethod]" to tell B to call its superclass version of someMethod?
 
And [super someMethod] means that it will run "someMethod" in the current class' superclass, right? There is no way to do something like "[B.super someMethod]" to tell B to call its superclass version of someMethod?

Not directly.

This is why categories/composing are preferred to subclasses in most regards.

You could probably do this via method swizzling, but I would recommend not ever doing this. (I have yet to think of a really good reason to do so)
 
And [super someMethod] means that it will run "someMethod" in the current class' superclass, right? There is no way to do something like "[B.super someMethod]" to tell B to call its superclass version of someMethod?

You might use NSObject's -methodForSelector: to get the vector for the implementation of the object's superclass, but you would have to create an instance of the superclass from which to get the instance method. Then you could call the super's method directly, as a function.

This is obviously a major hassle – and rightly so. Objects are designed to take care of themselves and be essentially opaque to the other objects that send them messages. If you find yourself needing to use a technique like this, I would suggest that there is a problem with your design, that you should review it and figure out a way to simplify and improve the design.
 
Obviously you think some, of those views are going to be NSControls (or subclasses). Which means you could either remove the if statement, OR better yet make it

for (NSControl *control in fields) ...


If only SOME of them are NSControls then everything looks good here.

I'm not sure what you're trying to say here. To give a little background on that snippet, "fields" is an array containing all of the objects in a window. So two things, can be said about that. First is that I KNOW all the objects are subclasses of NSView, the second is that some can potentially be subclasses of NSControl. That being said, when I tried the code you're proposing, I got runtime errors when the computer tried to treat an NSView as an NSControl, which caused the method to return prematurely and not implement the actions I had intended.
 
You can turn off warnings for deprecated methods and functions. Of course when you install the first 10.7 SDK, you shouldn't be using anything that is deprecated in 10.6 already (especially because it might be gone in 10.7).

Actually you can do better than that. While I think plain -Werror is bad, I do find -Werror -Wno-error=deprecated-declarations acceptable.

That way you still get the "this is deprecated" message without the "fix now now now" factor.
 
I'm not sure what you're trying to say here. To give a little background on that snippet, "fields" is an array containing all of the objects in a window. So two things, can be said about that. First is that I KNOW all the objects are subclasses of NSView, the second is that some can potentially be subclasses of NSControl. That being said, when I tried the code you're proposing, I got runtime errors when the computer tried to treat an NSView as an NSControl, which caused the method to return prematurely and not implement the actions I had intended.

Right... because they were not all NSControls...

I said that if only some of the objects in fields were NSControls, then your code was appropriate. My snippet would silence the compiler warning, but only work if all of the objects in fields were NSControls.

This is where intention comes into play, from just the snippet, I sorta assumed that "fields" would only contain NSControls. I would not have made that assumption if "fields" was named "views" or "subviews".
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.