I'm understanding how delegates work, had gone trough some examples, but now with a basic app for testing, it seems i haven't yet got it,
here my code:
Class defining protocol > *.h
#import <Foundation/Foundation.h>
@protocol protoPerra <NSObject>
-(void) dimeTuNombre:(NSString *) s;
@end
@interface MyClassic : NSObject {
id<protoPerra> _delegate;
}
@property (assign) id<protoPerra> delegate;
@end
Class implementing protocol> *.m
#import "MyClassic.h"
@implementation MyClassic
@synthesize delegate = _delegate;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
[[self delegate] dimeTuNombre:@"pinguete"];
}
return self;
}
-(void) dealloc {
[_delegate release];
_delegate = nil;
[super dealloc];
}
@end
Class Adopting protocol:
#import "MyClassic.h"
@interface MainViewController : UIViewController (protoPerra)
.m
#import "MainViewController.h"
@implementation MainViewController
-(void) dimeTuNombre {
NSLog(@"ss9!! tkt");
}
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *lava = [[[UILabel alloc] initWithFrame:CGRectMake(30, 100, 100, 20)] autorelease];
lava.text = @"lava ss9";
[self.view addSubview:lava];
MyClassic *pirr = [[[MyClassic alloc] init ] autorelease];
[pirr setDelegate:self];
}
-(void) dimeTuNombre:(NSString *)s {
NSLog(@"%@",s);
}
@end
so what is missing in this simple example to make it work with my delegate?
thanks a lot!
Please note i have used () insted of the <> [in the .h of the adopting class] as if i use the Chevrons, the code disappears
You call the delegate method (dimeTuNombre:) in MyClassic init, but the delegate is not set yet, so you either need to rethink when to call dimeTuNombre:, or refactor the MyClassic class to have a constructor like
-(id)initWithDelegate:(id<protoPerra>)delegate
instead of just simple init.
To clarify, in your current code you set the delegate in MainViewController.h when you do [pirr setDelegate:self]; - which sets the value "delegate" of the property of MyClassic to the current instance of MainViewController. Note that when you call MyClassic's init, the delegate isn't set yet, so the [[self delegate] dimeTuNombre:@"pinguete"]; call does nothing (delegate is nil at that point).
You could change your MyClassic constructor as follows:
-(id)initWithDelegate:(id<protoPerra>)aDelegate
{
self = [super init];
if (self) {
_delegate = aDelegate;
// Initialization code here.
[[self delegate] dimeTuNombre:@"pinguete"];
}
return self;
}
Then instead of this:
MyClassic *pirr = [[[MyClassic alloc] init ] autorelease];
[pirr setDelegate:self];
You will do this:
MyClassic *pirr = [[[MyClassic alloc] initWithDelegate:self ] autorelease];
This will work, but note that usually delegates are used for notifications of various kinds - like a button reporting it was clicked or a socket says it has received some data.
I think that Paul Hegarty from Stanford University gave a very good summary on how to use protocol in Lesson 9, development apps for iOS, fall session 2011(available in iTunes, check Paul's blog):
- Create the @protocol
- Add delegate @property to delegator's public @interface
- Use delegate property inside delegator's implementation
- Set the delegate property somewhere inside the delegate's @implmentation
- Implement the protocol's method(s) in the delegate(include <> on @interface)
Paul also gave an example of how to use protocol in a calculator app.
First, the protocol is created:
@protocol CalculatorProgramsTableViewControllerDelegate
@optional
- (void)calculatorProgramsTableViewController:
(CalculatorPorgramTableViewController *)sender
choseProgram:(id)program;
@end
Step 2). In the table view of CalculatorProgramsTableViewController.h, a delegate is defined as a weak linked id property:
@interface CalculatorProgramsTableViewController : UITableViewController
...
// Define a property delegate
@property (nonatomic, weak) id<CalculatorProgramsTableViewControlerDelegate>
delegate;
...
@end
By using the protocol, the table view will be able to send out message about a program is changed, but the view does not know about the graphic view. This is done in the next step.
Step 3). In the table view controller, the delegate is used to send message to out about the program changed:
#progma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView
didSeelectRowAtIndexPath:(NSIndexPath *)indexPath
{
id program = [self.programs objectAtIndex:indexPath.row];
[self.delegate calculatorProgramsTableViewController:self
choseProgram:porgram];
}
Step 4). Where is the delegate was set? It is set in the calculatorGraphViewController (right view in the split view). When a segue is ready to push a pop up view(table view), it sets self as a delegate:
@implementation CalculatorGraphViewController
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender
{
if ([segue.identifier isEqualToString:@"Show Favorite Graphics"]) {
NSArray * programs = [[NSUserDefaults standardUserDefaults]
objectForKey:FAVORITES_KEY];
[segue.destinationViewController setPrograms:programs];
[segue.destinationViewController setDelegate:self]; // set delegate
}
}
The graphic view does not know about the table view, the communication channel is built by the protocol, beautifully done without any coupling! In order for any delegate to call back by this protocol, the graphic view controller's .h file has to implement the protocol:
// .h to implement the protocol
@interface CalculatorGraphViewController :NSObject
<CalculatorProgramsTableViewControllerDelegate>
...
@end
And the method of protocol is defined in the .m file in the next step.
Last step. The delegate's protocol method is implemented in the segue's controller. The protocol's method will be called when a row in table view is selected:
// implement delegate method
- (void)calculatorProgramsTableViewController:(CalculatorProgramsTableViewController *)sender
chooseProgram:(id)program
{
self.calculatorProgram = program;
}
The above is a typical way to use protocol in iOS app.