Object casting

Discussion in 'Mac Programming' started by BadWolf13, Jan 10, 2011.

  1. BadWolf13 macrumors 6502

    Joined:
    Dec 17, 2009
    #1
    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?
     
  2. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #2
    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"
     
  3. BadWolf13 thread starter macrumors 6502

    Joined:
    Dec 17, 2009
    #3
    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?
     
  4. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #4
    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.
     
  5. BadWolf13 thread starter macrumors 6502

    Joined:
    Dec 17, 2009
    #5
    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.
     
  6. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #6
    You should always fix warnings. Do this:
    Code:
    [(NSControl *)view setEnabled:edit];
     
  7. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #7
    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.
     
  8. gnasher729, Jan 12, 2011
    Last edited: Jan 12, 2011

    gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #8
    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.


    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.
     
  9. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #9
    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.
     
  10. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #10
    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?".
     
  11. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #11
    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)?
     
  12. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #12
    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.
     
  13. smirk macrumors 6502a

    smirk

    Joined:
    Jul 18, 2002
    Location:
    Orange County, CA
    #13
    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()?
     
  14. gnasher729, Jan 12, 2011
    Last edited: Jan 12, 2011

    gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #14
    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).


    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.
     
  15. smirk macrumors 6502a

    smirk

    Joined:
    Jul 18, 2002
    Location:
    Orange County, CA
    #15
    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?
     
  16. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #16
    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)
     
  17. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #17
    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.
     
  18. BadWolf13 thread starter macrumors 6502

    Joined:
    Dec 17, 2009
    #18
    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.
     
  19. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #19
    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.
     
  20. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #20
    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".
     

Share This Page