Objective-C Protocols within Protocols - objective-c

I recently began trying my hand at using protocols in my Objective-C development as an (obvious) means of delegating tasks more appropriately among my classes. I completely understand the basic notion of protocols and how they work. However, I came across a roadblock when trying to create a custom protocol that in turn implements another protocol. I since discovered the solution, but I am curious why the following DOES NOT work:
#protocol STPickerViewDelegate < UIPickerViewDelegate >
- ( void )customCallback;
#end
#interface STPickerView : UIPickerView
{
id < STPickerViewDelegate > delegate;
}
#property ( nonatomic, assign ) id < STPickerViewDelegate > delegate;
#end
Then in a view controller, which conforms to STPickerViewDelegate:
STPickerView * pickerView = [ [ STPickerView alloc ] init ];
pickerView.delegate = self;
- ( void )customCallback
{
...
}
- ( NSString * )pickerView:( UIPickerView * )pickerView titleForRow:( NSInteger )row forComponent:( NSInteger )component
{
...
}
The problem was that pickerView:titleForRow:forComponent: was never being called. On the other hand, customCallback was being called just fine, which isn't too surprising. I don't understand why STPickerViewDelegate, which itself conforms to UIPickerViewDelegate, does not notify my view controller when events from UIPickerViewDelegate are supposed to occur. Per my understanding of Apple's documentation, if a protocol (A) itself conforms to another protocol (B), then a class (C) that conforms to the first protocol (A) must also conform to the second protocol (B), which is exactly the behavior I want and expected.
What I ended up doing was removing the id< STPickerViewDelegate > delegate property from STViewPicker and instead doing something like the following in my STViewPicker implementation where I want to evoke customCallback:
if ( [ self.delegate respondsToSelector:#selector( customCallback ) ] )
{
[ self.delegate performSelector:#selector( customCallback ) ];
}
This works just fine, but I really am puzzled as to why my original approach did not work.

The problem was that UIPickerView already has a delegate member variable, and you were declaring another one in a subclass which was what was being set, while the superclass's delegate variable remained nil and therefore any delegate methods would not be called on the class you expect it to be called on. In this case what you did is pretty much the only way to do it; if you need to extend the given protocol do so, have a class implement that, then just set the class as the UIPickerView's delegate.
Edit: btw, awesome avatar :)

I tried to do something similar - I assume you want to group extra methods you add to the UIImagePickerControllerDelegate in one file? I was running a UIImagePicker from two places and wanted it to behave the same way without duplicating code.
What I did was to add a category to the UIViewController, like this (below.) I'm fairly new to Objective-C (have used C++ for many years) so assuredly this probably violates the spirit of how you are "supposed" to do things (extending the protocol makes more sense), but my approach accomplished what I wanted, so I thought I'd toss it out.
UIViewController+imagePickerDelegate.h:
#interface UIViewController (ImagePickerDelegate) <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
-(void)configurePicker:(UIImagePickerController*)picker;
...
#end
UIViewController+imagePickerDelegate.m:
#import "UIViewController+imagePickerDelegate.h"
#implementation UIViewController (ImagePickerDelegate)
-(void)configurePicker:(UIImagePickerController*)picker
{
picker.delegate = self;
picker.allowsEditing = YES;
}
....
#end

Related

Objective C Subclassing: Is this proper?

So I'm working on an iPad game using the cocos2d framework.
My game requires different buildings to be spawned at the beginning of a "level". The user gets to choose which buildings they want to use.
An important functionality here is that different buildings will perform different actions when pressed.
I have a parent class that looks something like this:
BaseBuilding.h
#interface BaseBuilding : NSObject {
CGRect _hitBox;
NSString *_name;
CCSprite *_sprite;
int _identifier;
int _health;
int _coolDownDuration;
}
#property (atomic, retain) CCSprite *sprite;
#property (atomic, assign) int identifier;
#property (atomic, assign) int health;
- (void) onBuildingPressed;
- (id) initWithName:(NSString*)baseName;
- (id) initBuilding;
- (CGRect) hitBox;
- (void) sustainDamage;
So most of my buildings are very similar. Each building has their own initBuilding method which overrides the parent. The initBuilding method calls the super initWithName method which then looks up a plist with information about that building (sprite name, etc).
Each building also has their own onBuildingPressed method, which also overrides the parent method. This is very important functionality.
The part that I'm having trouble with:
I want to spawn the buildings based on what the player selects. What I'm currently doing is something like this:
BaseBuilding *building;
switch (choice) {
case 1:
building = [BuildingX alloc];
[building initBuilding];
break;
case 2:
building = [BuildingY alloc];
[building initBuilding];
break;
case 3:
building = [BuildingZ alloc];
[building initBuilding];
break;
}
return building;
Since BuildingX, BuildingY, BuildingZ are all subclasses of BaseBuilding, initBuilding does call the correct method.
Later on I can call [rightBuilding onBuildingPressed]; and it works as expected.
BuildingX has a removeAmmo method that the other buildings don't have, so I have to do this to call it: [(BuildingX*)centerBuilding removeAmmo].
Is this proper, or this a better way of doing this?
The only thing that I notice is that, if you have many subclasses - provided that all subclasses have some alphabetical order - you do it faster and more elegantly by getting a class object according to the value of choice:
NSString* className= [NSString stringWithFormat: #"Building%c",'X'-1+choice];
Class class= NSClassFromString(className);
BaseBuilding* building= [[class alloc] initBuilding];
But this is just a matter of style and rapidity (writing the code, I mean). Your method is perfectly fine and there's nothing wrong with it.
You can verify the class at run time to guarantee there's no room for error here like so:
if ( [centerBuilding respondsToSelector:NSSelectorFromString(#"removeAmmo")] ) {
[centerBuilding performSelector:NSSelectorFromString(#"removeAmmo"];
}
Seems logical to me. I don't know if it's stylish, the snobs can determine that for you. If it works, it works. I don't see the point in fixing things that aren't broken.
Yes, it's correct.
But:
1) If you are casting all your subclasses to the parent class, and you need to use a particular method of your subclass, then it's useless casting to the right subclass ONLY in certain circumstances. Try to make the removeAmmo a base method...
or
2) make your building subclass calling the removeAmmo method from its inside, and declare the removeAmmo method in a category
From an external point of view, the BaseClass has not the removeAmmo method, so it's not logic to declare a public method which could be called only re-casting your object to a specific subclass.

Objective-C: Where to initialize a delegate

I don´t fully understand how to use the Delegation pattern in obj-C. Basically I have a class: DigGameControlLayer.h and in it´s header I define a protocol with one required method that all users of this class needs to implement. Then I create the delegate property that I use within the code like any other property to delegate responsibility of what the moveObjectToPosition: method should do. So far so good I hope.
//DigGameControlLayer.h
#protocol DigGameControlLayerDelegate <NSObject>
-(void) moveObjectToNewPosition: (CCSprite *)object atSpeed:(float)moveSpeed;
#end
#property (assign) id <DigGameControlLayerDelegate> delegate;
Then the class that is using that class (in this case DigCharacter) says it adheres to the DigGameControlDelegate protocol
#interface DigGoblinPlayer : DigCharacter <DigGameControlLayerDelegate>
But what I don´t understand is where do i Initialize and set the delegate property a declared? Cause currently it does nothing when I use it in DigGameControlLayer since it´s null
[self.delegate moveObjectToNewPosition:object atSpeed:moveSpeed];
You can pass the delegate in the init method like so:
DigGoblinPlayer* player;
player = [[DigGoblinPlayer alloc] initWithName:(NSString*)name delegate:self];
Or set it separately:
DigGoblinPlayer* player;
player = [[DigGoblinPlayer alloc] initWithName:(NSString*)name];
player.delegate = self;
Which style you choose depends on if you always want/need a delegate, or if you want to be able to change/reset it later on.
In some cases you don't want the delegate to be a public property; then you'd use the first style.
You see a lot of example of this in the iOS SDK like here.
Note that self --which is just an example, and could an other object of course-- needs to implement this delegate. And name is something I made up.
in DigGoblinPlayer
implement the method
-(void) moveObjectToNewPosition: (CCSprite *)object atSpeed:(float)moveSpeed
{
}
this method will be called when the method calls in DigGameControlLayer
[self.delegate moveObjectToNewPosition:object atSpeed:moveSpeed];

Unable to access App Delegate property

I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:
LTAppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
}
#property Subject *selectedSubject;
#end
LTAppDelegate.m
#synthesize selectedSubject;
The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:
LTTableViewController.h
#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"
#interface LTTableViewController : NSObject{
NSMutableArray *notesArray;
LTAppDelegate *appDelegate;
Subject *s;
}
-(IBAction)currentSubjectDetails:(id)sender;
#end
LTTableViewController.m
#import "LTTableViewController.h"
#implementation LTTableViewController
- (id)init
{
self = [super init];
if (self) {
appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);
s = [appDelegate selectedSubject];
NSLog(#"Test Subject: %#", [s title]);
}
return self;
}
-(IBAction)currentSubjectDetails:(id)sender{
NSLog(#"Selected Subject: %#", [s title]);
}
After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.
Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.
Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.
If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.
Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.
Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).
Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.
-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).
Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.
If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.

NSArrayController adding categories

I'm trying to add new categories to the NSArrayController class: it can select the first and the last item. I did so:
#import "NSArrayController+selectEnds.h"
#implementation NSArrayController (selectEnds)
- (void)selectFirst:(id)sender {
if (self.arrangedObjects !=nil){ BOOL ignore = [self setSelectionIndex:0];}
}
- (void)selectLast:(id)sender {
if (self.arrangedObjects !=nil){
NSUInteger lastItem = [self.arrangedObjects count]-1;
BOOL ignore = [self setSelectionIndex:lastItem];}
}
#end
I get no errors, but I would like to put this object in IB, using a blue cube and binding buttons to its "selectFirst" and "selectLast" methods.
But I'm a bit lost: which standard object to start with? A standard ArrayController? And then, which class name to choose to have the new methods listed?
Thanks for your help…
Since you didn't show NSArrayController+selectEnds.h (which is what IB actually looks at), just NSArrayController+selectEnds.m, it's hard to know exactly what you got wrong, but there's two plausible guesses.
First, if you want these new methods to be part of the interface of the class NSArrayController, you have to add them to the interface declaration, not just to the implementation.
Second, if you want Xcode (or IB) to know that these new methods are actions, you have to label them as such: in the interface, instead of marking them plain void methods, mark them IBAction methods. (In the implementation, you can do either; it doesn't matter.)
So, NSArrayController+selectEnds.h should be:
#import <Cocoa/Cocoa.h>
#interface NSArrayController (selectEnds)
- (IBAction)selectFirst:(id)sender;
- (IBAction)selectLast:(id)sender;
#end

objective-c delegate

i working with geocoding at the moment. The geocoding service allways works with delegates.
So let's say, I've got a
AskingClass and AnsweringClass(geocoding)
The AskingClass calls a function in the AnsweringClass to return the adress of the current location.
AnsweringClass should handle and capsulate the geocoding stuff. My Problem is, with all these delegates, I do not manage to come back to the orginal function, which the asking class has called. So I cannot give easily the adress back:
AskingClass.Adress= [AnsweringClass giveAdress];
I managed it, doing it with delegates, so the result comes back in a delegate function (somewhere) in the askingClass. But I'm not happy with that. It's seems oversized and complex.
with best regards
Klaus-Dieter
It is unclear why you are using a delegate pattern at all. Why not just use straight up classes?
Something like this (assuming that you are using a PCH file for your header files or otherwise importing 'em as needed):
AnsweringClass.h
#interface AnsweringClass:NSObject
- (MyAnswer *)answerThisDude;
#end
AskingClass.h
#class AnsweringClass; // just in case you including AskingClass.h before AnsweringClass.h
#interface AskingClass : NSObject
// {
// declare the ivar if you need support for 32 bit "classic" ABI
// AnsweringClass *theThingThatAnswers;
// }
#property(retain) AnsweringClass *theThingThatAnswers;
#end
Then you can do this:
AskingClass.m
#implementation AskingClass
#synthesize theThingThatAnswers;
- (void) setUpMyStuff // probably invoked by your designated initializer or app launch handler
{
self.theThingThatAnswers = [AnsweringClass new];
MyAnswer *theFirstAnswer = [self.theThingThatAnswers answerThisDude];
}
// don't forget a -dealloc if you aren't running GC'd
#end
No delegation necessary.