using a delegate in a class method - iphone

I want to invoke a delegate in class method.
The example below obviously does not work, since the delegate is an instance variable that is accessed within a class method. (Error: instance variable 'delegate' accessed in class method)
Is there an alertnative?
My header file:
// MyClass.h
#import <Foundation/Foundation.h>
#protocol MyDelegate <NSObject>
-(void)update;
#end
#interface MyClass : NSObject
{
id<MyDelegate> delegate;
}
#property (nonatomic, retain) id delegate;
+(void)methodThatInvokesDelegate;
#end
My implementation file:
// MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize delegate;
+(void)methodThatInvokesDelegate{
[delegate update];
}
#end

Three obvious options:
Singleton
Static variable (i.e., class variable) pointing to the delegate
Use NSNotification's rather than delegates
Since a singleton (and a static variable) can't keep track of the lifecycle of delegates, I think option three would be the cleanest.

I want to know the context, which let you run in that situation. ;-) Anyway:
First: Delegates are set for a specific instance object. Because of this, you can have different delegates for different instances of the same (delegating) class.
Second: A class method runs inside a class object of that class. This is an object that is different from every instance object of that class. So there is nothing that can be called "the delegate". You can have 100s of delegates.
Third: Your class object needs a delegate at its own. So you have to add a property delegate to the class object and then use this. (Yes, it is possible to have properties an a class object. I did not write declared property.) If you need further information on how to do this, just comment it. I will add code.

I'm not sure if this will help you, but I have a similar situation where I have a class method used for data loads. In this case, the class instantiates itself (so that the caller doesn't need to) until it is done. (this code was edited somewhat to make it work here)
header file:
#protocol DataLoaderDelegate2 <NSObject>
- (void) dataLoaderSuccess:(NSData *)data loader:(id)theloader;
- (void) dataLoaderFailed:(NSString *)error loader:(id)theloader;
#end
#interface DataLoader2 : NSObject {
NSURLConnection *conn;
NSMutableData *receivedData;
NSFileHandle *fileHandle;
id <DataLoaderDelegate2> delegate;
}
#property (nonatomic, assign) id<DataLoaderDelegate2>delegate;
Call to start the process - the call to initWithRequest passes "self" along.
+ (DataLoader2 *)loadWithURLRequest:(NSURLRequest *)req delegate:(id)_delegate
{
DataLoader2 *dl = [[DataLoader2 alloc] init];
[dl setDelegate:_delegate];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
return dl;
}
When the data is done loading, it cleans up with something like
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if ([delegate respondsToSelector:#selector(dataLoaderSuccess:loader:)])
[delegate dataLoaderSuccess:(fileHandle)?(id)fileHandle:(id)receivedData loader:self];
[self autorelease];
}

Related

How to use #protocol in AppDelegate iPhone app?

I am working in iPhone app with 5 screens. I want to refresh the values in the screen 4th in UITabBarController. I have added #protocol in AppDelegate but it is not calling. This is the first time am using #protocol could you please help me to solve this issue,
In AppDelegate.h
#protocol ReloadViewControllerDelegate <NSObject>
-(void) refreshViewController:(NSString *)result;
#end
id refreshViewControllerDelegate;
#property (nonatomic, retain) id refreshViewControllerDelegate;
and i have synthesized.
In AppDelegare.m
#synthesize refreshViewControllerDelegate;
if ([refreshViewControllerDelegate conformsToProtocol:#protocol(ReloadViewControllerDelegate)])
{
[refreshViewControllerDelegate performSelectorOnMainThread:#selector(refreshViewController:) withObject:#"YES" waitUntilDone:NO];
}// Control not come inside of if Condition.... From here i want to update the fourthViewController..
But control not go inside of the if condition. Could you please guide me where am doing wrong?
In my 4th ViewController.h
#import "AppDelegate"
#interface fourthViewController : UIViewController <ReloadViewControllerDelegate>
In my 4th ViewController.m
-(void) refreshViewController:(NSString *)result
{
NSLog(#"Result : %#", result);
}
Can anyone please help me to do this? Thanks in advance.
You need to declare your delegate like this:
#property (nonatomic, weak) IBOutlet id<ReloadViewControllerDelegate> delegate;
The id will work, but by using the <>, you can make sure that the delegate you assign is actually implementing the protocol, you might still have to make sure it responds to selector but that is only if some methods are declared as
#optional
make sure you synthesize it and most important make sure you set it, and it is not nil.
You're getting a warning because you are typing your delegate as an id. An id is a generic type, which means the compiler has no idea of what methods or properties might be available. In order to remove your warning, declare your delegate to be an NSObject:
#property (nonatomic, retain) NSObject <ReloadViewControllerDelegate> *refreshViewControllerDelegate;
By declaring as an NSObject, the compiler now knows about all the methods NSObject has, which will then allow you to call:
-performSelectorOnMainThread:withObject:waitUntilDone:
on your delegate without warnings. Good luck!
Try this:
#protocol ReloadViewControllerDelegate <NSObject>
-(void) refreshViewController:(NSString *)result;
#end
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (weak) id <ReloadViewControllerDelegate>refreshViewControllerDelegate;
#end
In AppDelegate.m
#implementation AppDelegate
#synthesize window, refreshViewControllerDelegate;
...
here Tab4ViewController is name of class.
if ([Tab4ViewController conformsToProtocol:#protocol(ReloadViewControllerDelegate)])
{
[refreshViewControllerDelegate performSelectorOnMainThread:#selector(refreshViewController:) withObject:#"YES" waitUntilDone:NO];
}
...
#end
#import "AppDelegate.h"
#interface Tab4ViewController<ReloadViewControllerDelegate>
...
#end
#implementation Tab4ViewController
...
appDelegate.refreshViewControllerDelegate = self;
...
#end
You are calling this code:
if ([refreshViewControllerDelegate conformsToProtocol:#protocol(ReloadViewControllerDelegate)])
But refreshViewControllerDelegate is this:
id refreshViewControllerDelegate;
conformsToProtocol checks to see if the object declares that it conforms to the protocol, which yours does not. If you want to specify conformity to a protocol you need to:
id<ReloadViewControllerDelegate> refreshViewControllerDelegate;
EDIT
OK, on the performSelectorOnMainThread problem... That method is provided in a category for NSThread, and is not declared in the NSObject protocol. So, if you want to call that, then you need to declare your type as NSObject, which conforms to your protocol.
NSObject<ReloadViewControllerDelegate> refreshViewControllerDelegate;
EDIT
OK, it looks like this is not a simple question about using a protocol, but a full tutorial. Since SO isn't the place for such, I'll try to give a brief one...
A protocol is an interface declaration.
#protocol ReloadChatViewControllerDelegate <NSObject>
- (void)refreshViewController:(NSString *)result;
#end
That says there is a new protocol in town, with the name ReloadChatViewControllerDelegate and it also conforms to the NSObject protocol. Any class that adopts the new protocol must provide an implementation of refreshViewController. You can make a protocol method optional, by putting in an #optional section.
#protocol ReloadChatViewControllerDelegate <NSObject>
- (void)refreshViewController:(NSString *)result;
#optional
- (void)optRefresh;
#end
Now, let's leave the adoption of the protocol for later. Say you are writing generic code, and you just want to know if the object you are given conforms to the protocol, and if so, invoke a method on it. Something like...
#interface Bar : NSObject
#property (nonatomic, weak) NSObject<ReloadChatViewControllerDelegate> *refreshViewControllerDelegate;
- (void)blarg;
#end
Now, the Bar class is providing a delegate property, so that it can be give some object that will help it do some work. However, that delegate object must at least be an NSObject, and conform to the ReloadChatViewControllerDelegate protocol.
Now, ObjC (and C) is quite permissive, so you can force an object to be any type you want, but then you deserve the crash you get. Now, when blarg is called, the delegate is notified to do some work.
Since the property type of the delegate already says it conforms to the given protocol, there is no need to check for conformity. We can just call the delegate method. Note that we must see if the object implements any optional protocol methods.
#implementation Bar
#synthesize refreshViewControllerDelegate = _refreshViewControllerDelegate;
- (void)blarg {
// Do something, then invoke the delegate
[self.refreshViewControllerDelegate
performSelectorOnMainThread:#selector(refreshViewController:)
withObject:#"YES"
waitUntilDone:NO];
if ([self.refreshViewControllerDelegate respondsToSelector:#selector(optRefresh)]) {
[self.refreshViewControllerDelegate optRefresh];
}
}
#end
However, if you want to be generic, and accept any object as a delegate (maybe you want to make it optional that the delegate conforms to some given protocol), then you can accept a plain id and then check to see it it conforms. In that case, you could declare your delegate as just an id (or some other type).
#property (nonatomic, weak) id refreshViewControllerDelegate;
Now, in your code, you need to check for conformity.
- (void)blarg {
// Do something, then invoke the delegate
if ([self.refreshViewControllerDelegate
conformsToProtocol:#protocol(ReloadChatViewControllerDelegate)]) {
[self.refreshViewControllerDelegate
performSelectorOnMainThread:#selector(refreshViewController:)
withObject:#"YES" waitUntilDone:NO];
if ([self.refreshViewControllerDelegate
respondsToSelector:#selector(optRefresh)]) {
[self.refreshViewControllerDelegate optRefresh];
}
}
}
OK, now you have a protocol defined, and you have code that calls methods on the protocol. Two caveats.
First, the delegate has to be set to an object. nil will respond false for any method, so it will of course not conform, nor do anything when sent any message.
Second, you have to make sure that your delegate declares conformity to the protocol. Just implementing the methods is not conformity. If a class does not explicitly specify that is conforms to a protocol, then conformsToProtocol will return false, even if it implements the methods of the protocol.
So, let's specify some class that will act as our delegate by conforming to the protocol.
#interface Foo : NSObject<ReloadChatViewControllerDelegate>
- (void)refreshViewController:(NSString *)result;
#end
#implementation Foo
- (void)refreshViewController:(NSString *)result {
NSLog(#"Look, ma, I'm refreshed with %#", result);
}
#end
It conforms to the protocol, provides an implementation for the mandatory method, and omits the optional one.
Now, if you ran this code, you should see that marvelous code in all its splendor.
Foo *foo = [[Foo alloc] init];
Bar *bar = [[Bar alloc] init];
bar.refreshViewControllerDelegate = foo;
[bar blarg];

Passing data between ObjectClass and ViewController

I am trying to pass some data back to my view controller from an object class.
This is a basic over view of what classes and views are doing, and then I will show you my code.
So ViewController, loads its tableviewcells. Then inside this delegate method it calles a connection class I have created, inside that conection class is NSURLConnection methods connecting and downloading data from the database, in the connectionDidFinishLoading method of this connection class I set up a parsing class and pass all of the downloaded data over to that.
Then I parse that data, and at the end of that parser inside parserDidEndDocument, I am trying to send the data that is now in an array variable back to my view controller to display. However.. for some reason my protocols and delegates are not working.
I have set up protocols inside m parser class and set the delegates in my view controller but it never makes it to my protocol method.
I will show you my code below.
parserclass.h
#protocol PassParsedData <NSObject>
#required
- (void)sendManufactureArray:(NSArray *)array;
#end
//..
id <PassParsedData> delegate;
//..
#property (strong) id delegate;
//
parserclass.m
#import "VehicleSearchViewController.h"
//..
#synthesize delegate;
//..
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
//.. array stuff is set up and i can log it so i know its working.. i just want to show you the protocol and delegate stuff to be clear
[[self delegate]sendManufactureArray:filteredArray];
}
Then moving onto my view controller where I am hoping to call the protocol and get the filteredArray data back.
ViewController.h
#import "EngineResponses.h" //delegates & protocols
//..
#interface SearchViewController : UITableViewController <PassParsedData> {
//..
Viewcontroller.h
#import "EngineResponses.h"
//..
- (void)viewDidLoad
{
[super viewDidLoad];
//..
parserClass *pc = [[parserClass alloc] init];
[pc setDelegate:self];
//..
}
- (void)sendManufactureArray:(NSArray *)array //Breakpoint here is never accessed
{
FilterArray = array;
[self.tableView reloadData];
}
As you can see in that last method which is the protocol I am calling, its never accessed by the thread. I have checked, This call
[[self delegate]sendManufactureArray:filteredArray];
gets accessed fine.. but it just never makes it back to the View Controller.. any ideas... am I missing anything? .. im at a complete loss, been working on this all day.
any help would be HUGELY appreciated! :)
UPDATE:
I have added this to my ViewController.h
//..
ParserClass *parserclass;
//..
#property (strong, nonatomic) ParserClass *parserclass;
//..
viewcontroller.m
#synthesize parserclass;
//..
//then I call this in viewdidload
[engineResponses setDelegate:self];
As others have said in the comments, your pc variable is being deallocated at the end of the viewDidLoad block, since the only reference it has goes out of scope there. If you want it to live on, you must enlarge its scope (i.e. make it an instance variable).

protocol method is not being called by the delegate object

Another case of protocol method not being called - NO idea what am I doing wrong here...
Here is the code, omitting unnecessary info...
first header file: AccessNumber.h
#protocol AddItemDelegate <NSObject>
#required
- (void) processSuccessful: (BOOL)success;
#end
#interface AccessNumber : UIViewController <UITableViewDelegate, UITableViewDataSource, ABPeoplePickerNavigationControllerDelegate, UIAlertViewDelegate> {
id <AddItemDelegate> delegate;
}
#property (retain) id <AddItemDelegate> delegate;
#end
first .m file: AccessNumber.m - I am calling the protocol method from viewdidload just for testing purposes, it should basically get called from another method, but same thing for the sake of this convo (tried both of course)
#import "AccessNumber.h"
#import "History.h"
#synthesize delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
....
[[self delegate] processSuccessful:YES];
}
second file header: History.h
#interface History : UIViewController <UITableViewDelegate, UITableViewDataSource, AddItemDelegate> {
....
}
method implementation in history.m
- (void) processSuccessful: (BOOL)success {
NSLog(#"SUCCESS");
}
Appreciate any help. Thanks!
In the code i don't see something like:
theAccessNumber.delegate = self;
You must set the History instance as the delegate property of the AccessNumber instance.
Regards.
The code seems a little bit funny but you are not telling your "AccessNumber" class who is his delegate, even though you are making the "History" class implement the protocol established by yourself (otherwise you would get a warning).
You have to set the delegate for the "AccessNumber" class like this when setting it up from within "AccessNumber.m":
self.delegate = historyInstance;
or like this when setting it up from within "History.m":
accessNumberInstance.delegate = self;
There are very vew situations where you should retain a delegate, normally your delagate will outlive the object. So change your property from retain to assign. And be sure you are setting the delegate. Where are you doing it? If your object really depends on it you should be passing it in the constructor (iniWithDelagate). Try doing a NSLog before calling the delagate method just to see if it isn't nil.

ARC issue - cannot pass method to delegate?

Here's my scenario. I have a class A. Inside its implementation I create object of type B and set B's delegate to self (So B.delegate = self somewhere inside class A's implementation).
And class A has an instance method - (void)printThis;
Now inside B's implementation, when I try to do [delegate printThis];, it gives me this error:
"No known instance method for selector printThis"
Of course this is when I have enabled ARC. The above delegation pattern used to work fine in iOS 4.x without the ARC. And it still does when I switch OFF ARC. What has ARC got to do with passing messages to delegates?
Skeleton code:
A.h
#class B;
#interface A: blah blah
{
B objB;
}
-(void) printThis;
A.m
objB = [[B alloc] init];
objB.delegate = self;
- (void)printThis {
//doSomething
}
B.h
#interface B: blah blah
{
//id delegate; //used to be there, now I just property & synthesize
}
#property (nonatomic,weak) id delegate;
B.m
#synthesize delegate;
[delegate printThis]; //error with ARC ON, works with OFF
IMPORTANT EDIT:
And mind you this happens for a method here and there. For instance I have a few other methods in A like printThat etc etc which work without errors. I'm clueless as to what is happening!
You need to define -printThis in a protocol and make A implement this protocol. You also need to mark the delegate as conforming to this delegate.
i.e.:
#protocol Printer <NSObject>
- (void)printThis;
#end
#interface A : NSObject <Printer>
//...
#end
#interface B : //...
#property (nonatomic, weak) id<Printer> delegate;
#end
ARC needs to know about the interface for method calls in order to properly manage the memory correctly. If there isn't a definition then it'll complain.

how to extend a protocol for a delegate in objective C, then subclass an object to require a conforming delegate

I want to subclass UITextView, and send a new message to the delegate. So, I want to extend the delegate protocol. What's the correct way to do this?
I started out with this:
interface:
#import <Foundation/Foundation.h>
#class MySubClass;
#protocol MySubClassDelegate <UITextViewDelegate>
- (void) MySubClassMessage: (MySubClass *) subclass;
#end
#interface MySubClass : UITextView {
}
#end
implementation:
#import "MySubClass.h"
#implementation MySubClass
- (void) SomeMethod; {
if ([self.delegate respondsToSelector: #selector (MySubClassMessage:)]) {
[self.delegate MySubClassMessage: self];
}
}
#end
however with that I get the warning: '-MySubClassMessage:' not found in protocol(s).
I had one way working where I created my own ivar to store the delegate, then also stored the delegate using [super setDelegate] but that seemed wrong. perhaps it's not.
I know I can just pass id's around and get by, but My goal is to make sure that the compiler checks that any delegate supplied to MySubClass conforms to MySubClassDelegate protocol.
To further clairfy:
#interface MySubClassTester : NSObject {
}
#implementation MySubClassTester
- (void) one {
MySubClass *subclass = [[MySubClass alloc] init];
subclass.delegate = self;
}
#end
will produce the warning: class 'MySubClassTester' does not implement the 'UITextViewDelegate' protocol
I want it to produce the warning about not implementing 'MySubClassDelegate' protocol instead.
The UITextView defines its delegate as
#property(nonatomic, assign) id<UITextViewDelegate> delegate
meaning it conforms to UITextViewDelegate, and that's what compiler checks. If you want to use the new protocol, you need to redefine delegate to conform to your protocol:
#interface MySubClass : UITextView {
}
#property(nonatomic, assign) id<MySubClassDelegate> delegate
#end
The compiler shouldn't give any more warnings.
[Update by fess]
... With this the compiler will warn that the accessors need to be implemented... [I implemented this:]
-(void) setDelegate:(id<MySubClassDelegate>) delegate {
[super setDelegate: delegate];
}
- (id) delegate {
return [super delegate];
}
"
[My update]
I believe it should work if you only make a #dynamic declaration instead of reimplementing the method, as the implementation is already there:
#dynamic delegate;
For anyone still interested, this can be done quite simply like this (for sake of the example, I subclass UIScrollView):
#protocol MySubclassProtocol <UIScrollViewDelegate>
#required
-(void)myProtocolMethod;
#end
#interface MySubClass : UIScrollView
#property (nonatomic, weak) id <MySubclassProtocol> delegate;
The most important detail here is the part between the <> after your protocol's name which, put in a simple manner, signals you're extending that protocol.
In your implementation, all you need to do then is:
#synthesize delegate;
And you're done.
You need to extend the super protocol:
#protocol MYClassProtocol <SuperClassProtocol>
-(void)foo;
#end
after that DON'T (!!!) create the #property for the delegate otherwise you override the original delegate object, but simply override the method:
- (id<MYClassProtocol>)delegate
{
return (id<MYClassProtocol>)[super delegate];
}
now you can use the delegate in the classic way:
[self.delegate foo];
[self.delegate someSuperClassDelegateMethod];
Given that MySubClassMessage: is optional, you should be able to simple do a simple:
- (void) SomeMethod {
SEL delegateSelector = #selector(MySubClassMessage:);
if ([self.delegate respondsToSelector:delegateSelector]) {
[self.delegate performSelector:delegateSelector withObject:self];
}
}
The complier should still check that the implementing class conforms to your protocol (or at least claim to in the header) and you won't get the error you described.