This is from a book on iphone game development.
[((GameState*)viewController.view) Update];
"viewController" is an instance of UIViewcontroller, "GameState" is a subclass of UIView, and "Update" is a method of "GameState". Can you please tell me what is happening. Does this syntax allow the viewController to use the methods of GameState? I apologize if this is a stupid question.
All that's doing is telling the compiler "hey, viewController.view is actually of type GameState*". It doesn't actually do anything to it though, just lets the compiler know so it won't warn about it.
Note that it's entirely legal to lie to the compiler like this, and it will believe you, and not check your work, so it's best to avoid casting if you can. If you cast it to something it isn't, it will crash if you try to use methods it doesn't have.
What's going on here is a C type cast: you are telling the compiler that you know that your viewController's view is of type GameState, and that you know that it's OK to invoke methods of GameState here, even though these methods are not part of the UIView's interface.
Means that viewController's view is casted to a GameState (subclass of UIView) and in this way the compiler does not complain that Update method is invoked.
This has the inconvenience of potentially generating a runtime error so to be safe I will enclose the previous statement in:
if ([viewController.view isKindOfClass:[GameState class]])
Related
Is it possible to cast an object in Objective-C so as to tell the compiler that its type could be one of many?
For example, in my answer to iOS: Two Gestures, One Target-Action, I know an object will either be a UITapGestureRecognizer or a UILongPressGestureRecognizer but am not sure which one. And, both of those classes respond to numberOfTapsRequired but not through a common protocol. They just both implement the same property.
So, to get around compiler errors, I just cast the object as UILongPressGestureRecognizer. This works now, but if a future version of the iOS SDK changes the name of the UITapGestureRecognizer numberOfTapsRequired property (and left that of UILongPressGestureRecognizer unchanged), then my code would compile but crash with an unrecognized selector exception on a double-tap.
So, if there were a way I could tell the compiler, "Hey, I know this object is either one of two types," then that would allow me to make an accurate cast.
If you can't do this in Objective-C, do any programming languages allow this? I hear C# pretty much lets you do anything.
I don't think that a "multiple cast" exists in Objective-C, but you could use something like this to catch this issue at compile-time.
if([gestureRecognizer isKindofClass: [UITapGestureRecognizer class]]) {
(UITapGestureRecognizer*)gestureRecognizer.numberOfTapsRequired;
}
else if([gestureRecognizer isKindofClass: [UILongPressGestureRecognizer class]]) {
(UILongPressGestureRecognizer*)gestureRecognizer.numberOfTapsRequired;
}
I'm sure the answer to this question is embarrassingly basic, but I'm having trouble understanding how the real-time compiling / error-checking in XCode is supposed to work with the dynamic nature of Objective-C.
For example, I want to setEditing:YES for the tableView of whatever the topViewController is in my stack of view controllers. So I try this:
[self.navigationController.topViewController.tableView setEditing: YES animated: YES];
And XCode complains: Property 'tableView' not found on object of type 'UIViewController'.
Now, this code is in a UIViewController, but it would only be called when the topViewController is a UITableViewController, but obviously Xcode doesn't know that.
How do I fix this? Is this indicative of a bad coding practice on my part? I tried wrapping the line in a conditional to test that topViewController.tableView != nil, but Xcode then just bitches about the conditional line :)
EDIT: Thanks to answers by saadnib and Caleb below, this is what I have now:
if ([self.navigationController.topViewController isKindOfClass:([UITableViewController class])] ) {
UITableViewController *topController = (UITableViewController *)self.navigationController.topViewController;
[topController.tableView setEditing: YES animated: YES];
}
Actually you can access the property of topViewController by typecasting it. For example your topViewController name is "FirstViewController" then you can do this as
FirstViewController *fvc = (FirstViewController*)self.navigationController.topViewController;
[fvc.tableView setEditing: YES animated: YES];
i hope this will help you.
You get the error because self.navigationController.topViewController returns a pointer of type UIViewController*, and UIViewController doesn't have a tableView property. #saadnib's answer is correct: if you know that the pointer will always point to a certain UIViewController subclass, you can cast it to that type.
However, even though you "know" that the top view controller will always be a table view controller, you might want to check at run time that that's the case. You could use -isKindOfClass: to see if the controller is a subclass of UITableViewController. You'd still need the cast, of course, but it'd be a little safer.
I've just come across some code in Three20 that looks like this:
SEL sel = #selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];
}
On LLVM 2.0, this causes the compilation error:
error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI
I know why that error is occurring and I know how to fix it. I just need to invoke the method directly, like so:
SEL sel = #selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];
}
My question is, if you know both the selector and its arguments at compile time, why would you need to use performSelector:withObject:withObject: at runtime? I don't see why the code was written this way in the first place. If the selector and arguments were dynamically passed into the method, I may understand, but they're not, the selector and its arguments are hard coded, (even if the index does change during run time, its method of obtaining the index is hard coded.)
If someone could explain to me a good reason why this would be necessary, I'd be grateful. Otherwise, I'll be over here changing all this code.
After a little more digging, it looks like the TTPickerTextField class that this code is found in is an indirect subclass of a UITextField.
As such, it is piggy-backing on UITextFields delegate property, which doesn't conform to the TTPickerTextFieldDelegate protocol where the method textField:didAddCellAtIndex: is declared.
I have come to the conclusion that this code is just laziness. No reason why the UITextFields delegate property had to be piggy-backed, making this confusing, error prone code necessary.
My own approach would have been to leave UITextFields delegate property alone, and add my own property in my specific subclass that handled the specific delegate methods.
Just to clarify - the 'solution' I mentioned in the question fixes the compiler error, but generates a warning that the method can't be found and will be assumed to return id. This is what the original code was 'solving' but that only worked in GCC. No longer with LLVM 2.0.
Last edit, I promise:
My final solution to combat this laziness and get rid of the warning and error is an ugly hack:
[(id <TTPickerTextFieldDelegate>)self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];
Cast UITextFields delegate to an id that conforms to TTPickerTextFieldDelegate and then invoke the method directly.
Please don't be lazy :(
That respondsToSelector/performSelector combo is an idiom for optional delegate methods. The delegate isn't guaranteed to have that method defined, so a direct call to it would cause a compiler warning.
What the compiler was actually complaining about in this case:
[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];
error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI
is risky pointer arithmetic... 'id' is a pointer type, so:
(id)_cellViews.count-1
tells the compiler it's going to subtract one from a pointer instead of an integer....which is probably not the intent of that code. The withObject argument of performSelector has to be a pointer, it can't be a primitive. You can get around this by wrapping _cellViews.count - 1 in an NSNumber, and unwrapping it in the delegate method.
[self.delegate performSelector:sel withObject:self withObject:[NSNumber numberWithInt:_cellViews.count-1]];
I am working on a project where I have a class which has UIView property. I also define a class which is a subclass of UIView which defines a certain method. If I have the following code, I get a warning when I build:
// In this example, myView is UIView property which *may* contain a UIView or
// my subclassed-UIView which has the myMethod method
if([myView respondsToSelector:#selector(myMethod)]){
[myView myMethod]
}
The warning is "UIView may not respond to '-myMethod'". The warning obviously doesn't stop the app from being built, but I am just trying to figure out how to deal with it. Is this the correct way to do this? Is there a way to stop this warning?
The warning is only because the compiler doesn't know if that view is your custom subclass. Of course, at runtime it will work fine, since it will be a subclass. You have two options to fix it:
[myView performSelector:#selector(myMethod)];
(So the compiler doesn't check the method call at all)
Or, better:
[(MyViewClass *)myView myMethod];
That way the compiler acts as if the object really is your view subclass (after you performing the check of course).
For that matter, it might make sense to check for your class rather than the method:
if ([myView isKindOfClass:[MyViewClass class]]) { ...
You can use:
[myView performSelector:#selector(myMethod)];
This is a static typing warning, telling you that the type the variable is declared as does not respond to that selector. Since you're actually using a subclass that you've confirmed responds to the selector, you know this isn't a problem, but the compiler isn't smart enough to figure this out. There are a few ways you can fix this. In decreasing order of safety:
Cast the variable to what it actually is that does respond to the selector, either a specific class or a protocol. You'll still need to import the appropriate header or the compiler will suspect you mistyped something. Which option is best depends on your situation (e.g. whether there's one "correct" class to cast to).
[(id<SomeProtocolWiththatSelector>)myView myMethod];
[(SomeUIViewSubclass *)myView myMethod];
Cast the variable to id to disable static typechecking. You'll still need to import a header with the declaration so the compiler knows some method exists or it will still give the "I'm not sure if this is a real method" warning.
[(id)myView myMethod];
Use performSelector:. This will not do any checks at compile-time, so you don't need to import any headers besides Foundation, but the compiler won't catch any typos either, so any mistakes you make mean the program goes boom at runtime.
[myView performSelector:#selector(myMethod)];
The question is not only regarding the headline, but more of a "how will I achieve this, without trying to force a Java/Flash design into an Objective C (iPhone)program".
I have 6 views that extends UIView, these views all have different behavior but share certain methods, like -(void) update and -(void) changeState:(NSInteger)state.
A viewController, whose job is it to update, instantiate and display these views has a switch block to do this. So switch(4) {...} instantiates UIView4, but as I need a reference to the currently instantiated view (to do update and changeState:), I have a UIView property on my ViewController called self.currentView. As the instantiated UIView4 extends UIView I can easily go [self.currentView addSubview:UIView4instance] and then release the UIView4instance.
Now how will I call the [UIView4instance update] method on the view? or the [UIView5instance changeState] etc. etc.
Since I added it to self.currentView which is of type UIView it no longer has any reason to believe it has an update or changeState: method, meaning I cannot iterate the views and send them these messages.
This approach brings on a ton of other problems, I would need to test and typeCast my views each time I needed to do any operations on them.
If I were doing something like this Composite Pattern approach in, say, Java. I would either write an interface that all the views (UIView1, UIview2.... UIViewN) would implement. Or maybe an abstract class that all the views inherited the changeState: and update methods from.
This way I could have self.currentView just know that I'm adding objects to your view and they all conform to these methods.
The two solutions I can think of, with my very small Objective-C experience is:
doing it with delegation or categories, but this seems overkill in every way :/
I guess I could also extend UIView and then extend my class extending UIView again, but there is probably a reason Objective-C does not directly support abstract classes...
Hope someone could point me in the right direction regarding these issues.
Thanks:)
Yes it is equal. You can declare a protocol
#protocol MyViewProtocol
-(void)update;
-(void)changeState:(NSInteger)state;
#end
and declare your subclasses like
#interface MyUIView3 : UIView<MyViewProtocol> {
....
}
....
#end
Then, the declaration
UIView<MyViewProtocol>* view=[[MyUIView3 alloc] init];
means that view is an instance (of a subclass of) UIView which also conforms to MyViewProtocol.
Just the way it works in Java, I think. See the apple doc on protocols.
One thing to be aware of is that while defining a protocol like this is a convenience and certainly makes things clearer, it is by no means necessary to make your solution work. Objective-C binds methods at runtime, and so all you really need to do is to make sure all your view classes implement the methods you care about and call them.
You will get a complier warning for this, but it will work.
Now, in general it's probably preferable to define a protocol like this and it's what I generally do. But it's also important to remember that runtime binding is there and can be incredibly powerful.