Way to ignore "UIViewController may not respond to [method]" warning - iphone

Is there a way to make the compiler ignore this specific warning?
Here's what I do:
UIViewController *firstViewController = AppDelegate.instance.viewController;
//open the view of the clicked subItem
if ([firstViewController respondsToSelector:#selector(openView:inView:)]) {
[firstViewController openView:subItem.itemText.text inView:activeScreen]; //warning on this line
}
I know one way that works is to change UIViewController to ViewController (Name of it's class). But this fix won't work in the future, so I'm just looking for a way to ignore this warning.
It won't work in the future because, I'll be doing something like this:
//.m
UIViewController *firstViewController;
//.h
if (someCondition) {
firstViewController = AppDelegate.instance.viewController;
}
else{
firstViewController = AppDelegate.instance.otherViewController;
}
if ([firstViewController respondsToSelector:#selector(openView:inView:)]) {
[firstViewController openView:subItem.itemText.text inView:activeScreen]; //warning on this line
}

You should cast the object to the correct type where appropriate. Note that you can 'cast' to a protocol if you like. This gives you the safety of knowing that required methods are implemented without having to know the concrete type.
If you want to just have the compiler not complain, it's possible by calling performSelector:. But then you won't get compile-time checking.
[object performSelector:#selector(doSomething)];
See discussion: Using -performSelector: vs. just calling the method
If you want to pass exactly one object to your selector, it's possible by using the variant performSelector:withObject:.
If you want to pass multiple objects, you'll have to wrap them up in a container object, as described at iOS - How to implement a performSelector with multiple arguments and with afterDelay?.

In this case, you can just issue an explicit type conversion (cast):
UIViewController *firstViewController;
// ...
[(FirstViewController *)firstViewController openView:subItem.itemText.text inView:activeScreen];

Make sure to import the FirstViewController.h, so that method is known to the compiler. Tweak your code a bit:
UIViewController *vc = AppDelegate.instance.viewController;
//open the view of the clicked subItem
if ([vc respondsToSelector:#selector(openView:inView:)]) {
FirstViewController *firstViewController = (FirstViewController *) vc;
[firstViewController openView:subItem.itemText.text inView:activeScreen];
}
That should do the trick.

Related

Making Xcode complain about a missing parameter

I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.

How to interact between classes

A very basic question on how to interact between classes here: how can I trigger an action called by clicking on a button linked to one class (the graphic user interface in my case - which does not contain any drawing code) inside another class (my drawing class - which is defined programmatically)?
Thanks!
Edited: I have tried to implement the solutions suggested below but I didn't manage to trigger the action from the other class. I have two classes: the main view controller and a class with the drawing code. Any advice would be highly appreciated. Thanks!
//MainViewController.m
//This class has a xib and contains the graphic user interface
- (void)ImageHasChanged
{
//do something on the GUI
}
//DrawView.m
//This class has no associated xib and contains the drawing code
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//I want to call ImageHasChanged from MainViewController.m here
//How can I do this?
}
Inter-class functionality is done simply by importing one class into the other, and calling an accessible method/instance variable on the import.
For the button IBAction example in your question:
ClassA.m (This will be imported via its header):
#import "ClassA.h"
#implementation ClassA
// This is a class-level function (indicated by the '+'). It can't contain
// any instance variables of ClassA though!
+(void)publicDrawingFunction:(NSString *)aVariable {
// Your method here...
}
// This is a instance-level function (indicated by the '-'). It can contain
// instance variables of ClassA, but it requires you to create an instance
// of ClassA in ClassB before you can use the function!
-(NSString *)privateDrawingFunction:(NSString *)aVariable {
// Your method here...
}
#end
ClassB.m (This is your UI class that will call the other method):
#import "ClassA.h" // <---- THE IMPORTANT HEADER IMPORT!
#implementation ClassB
// The IBAction for handling a button click
-(IBAction)clickDrawButton:(id)sender {
// Calling the class method is simple:
[ClassA publicDrawingFunction:#"string to pass to function"];
// Calling the instance method requires a class instance to be created first:
ClassA *instanceOfClassA = [[ClassA alloc]init];
NSString *result = [instanceOfClassA privateDrawingFunction:#"stringToPassAlong"];
// If you no longer require the ClassA instance in this scope, release it (if not using ARC)!
[instanceOfClassA release];
}
#end
Side note: If you're going to require ClassA a lot in ClassB, consider creating a class-wide instance of it in ClassB to re-use wherever it's required. Just don't forget to release it in dealloc (or maybe set it to nil in ARC) when you're finished with it!
Finally, please consider reading through the Apple Docs on Objective-C classes (and indeed all other sections of the documentation relevant to what you're trying to achieve). It is a bit time-consuming, but very well invested in the long run into building your confidence as an Objective-C programmer!
//As you said an instance of MainViewController has to be created first
MainViewController *instanceOfMainViewController = [[MainViewController alloc]init];
[instanceOfMainViewController ImageHasChanged];
//Thanks for your help Andeh!
Actually you can use #protocol(Delegate) to interact message between two classes this is standard way Or refer this document
http://developer.apple.com/library/ios/#documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html to learn more

objective-c call function from another class

I have a function on a class "loadingViewController" that needs to be accessed from other classes. First time that I call function like follows, it works but if I then call it again from another class do not because allocates it again and reset parameters. Same if I create an instance method. How to simply call a function from another class without init or allocate again? Probably basic newbie issue... Thanks.
class was declared in header and properly synthesized.
self.loadingController = [[loadingViewController alloc] initWithNibName:#"loadingViewController" bundle:nil];
[loadingController incrementProgress:0.1];
Hard to say for sure without seeing more code, but I'm thinking you just need to make sure you only initialize the loadingController once:
if ( self.loadingController == nil ) {
self.loadingController = [[loadingViewController alloc] initWithNibName:#"loadingViewController" bundle:nil];
}
[self.loadingController incrementProgress:0.1];
You can implement protocols here. Protocols are used to call methods of another class from one class. In general it will define the set of methods which your class will implement. TO see how to implement it you can see this answer.
I would do this:
-(void) loadingViewController
{
if ( self.loadingController == nil ) {
self.loadingController = [[loadingViewController alloc] initWithNibName:#"loadingViewController" bundle:nil];
}
[self.loadingController incrementProgress:0.1];
}
AND make sure you don't call [xyz loadingViewController] from any other thread than the main UI thread.
It looks like the reason you want to call a function on a view controller is to present the progress of a long operation to the user.
The more common approach is to have the view controller start the operation and then observe it's progress, updating the view accordingly.

Setting a delegate using blocks in iPhone

On a view controller I have multiple textfields, which all use the same delegate. Now in the delegate the code gets really ugly since I have to differentiate between all the textfields (bunch of if/else-if or a switch statement). I came a cross this article:
Blocks in textfield delegates
But from this I still don't understand how this solves the problem? Doesn't this basically call one method and pass it the text and the method has no idea what textfield gave the string? You would still need to differentiate between the textfields, but this time inside the block (with the usual if(textfield == bazTextField)...).
I don't know that it exactly solves the problem so much as shifts it (and into viewDidLoad, which usually gets a bit of mush-mash in it anyway).
However in that example the block itself was being passed in the textfield to run comparisons with and "remembers" the values of all the instance variables as well (if it refers to them), so that's how it knows what text and text field is being dealt with.
I don't see how that code exactly is supposed to help though, since it assigns one block to the single delegate class to be used with all text field delegates - unless perhaps you were supposed to have one per text field, each with a different block. Then you have way more code than you'd have had with the if statements!
The article doesn't make it clear, but I believe the idea is to create one of these blocks (and block delegate objects) for each UITextField that you wish to have respond to textFieldShouldReturn.
hm, maybe I didn't completely understand the article, but I don't see the advantage of using blocks instead of selectors in that concrete example.
you could achieve something similar like this
#interface AlternativeTextFieldDelegate : NSObject <UITextFieldDelegate>
{
SEL selectorToCall;
id objectToCall;
}
- (void) setObjectToCall:(id)obj selector:(SEL)selector;
#end
#implementation AlternativeTextFieldDelegate
- (void) setObjectToCall:(id)obj selector:(SEL)selector
{
objectToCall = obj;
selectorToCall = selector;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[objectToCall performSelector:selectorToCall];
return YES;
}
#end
and the view controller
#interface ViewWithTextFieldsController : UIViewController
{
UITextField *tf1;
AlternativeTextFieldDelegate *delegateForTF1;
UITextField *tf2;
AlternativeTextFieldDelegate *delegateForTF2;
}
// ...IBOutlets and all that...
- (void) tf1ShouldReturn; // handles shouldReturn for tf1
- (void) tf2ShouldReturn; // handles shouldReturn for tf2
#end
#implementation ViewWithTextFieldsController
- (void) viewDidLoad // or wherever
{
delegateForTF1 = [[AlternativeTextFieldDelegate alloc] init];
[delegateForTF1 setObjectToCall:self selector:#selector(tf1ShouldReturn)];
tf1.delegate = delegateForTF1;
delegateForTF2 = [[AlternativeTextFieldDelegate alloc] init];
[delegateForTF2 setObjectToCall:self selector:#selector(tf2ShouldReturn)];
tf2.delegate = delegateForTF2;
}
// ...
#end
don't really know if that's any better than chaining if-elses though.
it seems to me that this complicates things more than the problem it solves.

Learning Objective-C: Need clarification on what is happening in this code block

I've been studying the sample code Apple provides in their iPhoneCoreDataRecipes application and there are a couple of things they they're doing that I don't understand and I haven't been able to find a resource to help me with them.
Specifically, the block of code in question is:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger section = indexPath.section;
UIViewController *nextViewController = nil; //Why this as opposed to alloc and init?
/*
What to do on selection depends on what section the row is in.
For Type, Instructions, and Ingredients, create and push a new view controller of the type appropriate for the next screen.
*/
switch (section) {
case TYPE_SECTION:
nextViewController = [[TypeSelectionViewController alloc] initWithStyle:UITableViewStyleGrouped];
((TypeSelectionViewController *)nextViewController).recipe = recipe;
break; //Why this as opposed to nextViewController.recipe = recipe???
case INSTRUCTIONS_SECTION:
nextViewController = [[InstructionsViewController alloc] initWithNibName:#"InstructionsView" bundle:nil];
((InstructionsViewController *)nextViewController).recipe = recipe;
break;
case INGREDIENTS_SECTION:
nextViewController = [[IngredientDetailViewController alloc] initWithStyle:UITableViewStyleGrouped];
((IngredientDetailViewController *)nextViewController).recipe = recipe;
if (indexPath.row < [recipe.ingredients count]) {
Ingredient *ingredient = [ingredients objectAtIndex:indexPath.row];
((IngredientDetailViewController *)nextViewController).ingredient = ingredient;
}
break;
default:
break;
}
// If we got a new view controller, push it .
if (nextViewController) {
[self.navigationController pushViewController:nextViewController animated:YES];
[nextViewController release];
}
}
I'm wondering if the ((name *)name).thing = aThing is different than name.thing = aThing? I'm sure it is, but I can't find any documentation to help me understand what they are doing here and why?
Also, why do they set the nextViewController equal to nil as opposed to just allocating and initializing it? Is there a reason that this was done here, while in other areas when creating a temporary view controller they used alloc and init?
Thanks in advance for your help!
I'm going to answer your questions in the order they come up in the code rather than the order you asked them, because the order is important here.
The reason we don't create the view controller when we first declare the variable is because we don't know what we want to create yet. Think about it: You want to write alloc] init]. But what goes before the alloc? We don't really know yet. We know it's some subclass of UIViewController, but we don't know the actual class. That's what the next part of the code is for.
So now we get to the main logic of the method. We're assigning the view controller's recipe, but there's a problem here: We declared the variable as UIViewController*, and UIViewController doesn't have a recipe property. We know our subclass does, but we already told the compiler that this variable pointed to a UIViewController. So in order for the compiler to let us do this property assignment, we need to cast the variable to the right class.
Incidentally, we could also just have declared the variable as id nextViewController and then write [nextViewController setRecipe:recipe] and there wouldn't be any need to cast. The downside is that then the compiler wouldn't be able to typecheck our code — we could accidentally assign an NSString to nextViewController in some little-used branch of the code and we'd never know until we happened to hit that branch at runtime.
It's due to inferred types and how the compiler handles dot-syntax.
The compiler considers nextViewController to be a UIViewController, since that's how it is declared. You can set it to be anything you want (although it should only really be set to UIViewController instances, UIViewController subclass instances or nil), but the compiler thinks of it as a UIViewController. When you use the Obj-C 2.0 dot-syntax:
myObject.recipe = recipe;
The compiler checks the type of myObject, and sees if it has a setRecipe: method declared. If it does, that statement is translated to the equivalent of:
[myObject setRecipe:recipe];
Then compiled. If there's no declaration of setRecipe:, it will attempt to treat the dot-syntax as a struct element, which will almost certainly throw a compiler warning. The type cast (TypeSelectionViewController *) tells the compiler to treat the variable as something of that type, so when you do:
((TypeSelectionViewController *)nextViewController).recipe = recipe;
The compiler looks to see if there's a setRecipe: method declared on the TypeSelectionViewController class, not on whatever nextViewController was declared as.
((TypeSelectionViewController *)nextViewController).recipe
nextViewController is a UIViewController* which doesn't have a recipe property. ((TypeSelectionViewController *)nextViewController) casts nextViewController to a TypeSelectionViewController*.
nextViewController is set to nil outside the switch block so that it is ready to go for each block within the switch. Notice how within each case block, it is the alloc and init are on a different type: TypeSelectionViewController vs InstructionsViewController etc.