Call back a function in the original class - iphone

I found some useful information on the web but I still can't manage to do it by myself ;)
Ok, let me put my problem in context for you :
I have a first class (myViewController) whose declaration is below :
// i just give you relevant information
RssParser *rssParser;
UIActivityIndicator *activityIndicator;
In my viewDidLoad method (myViewController) I have :
//once again, I summarize it for you
[activityIndicator startAnimating];
[rssParser parse];
Now, suppose you are in the parserDidEndDocument method (which is in RssParser class), how am I suppose to stop the animation of the activityIndicator ? How can I get access to my instance of activity Indicator from the RssParser class ?
Hope I made myself clear ;)
Thanks guys!
Gauthier

A common way to do this is to use a delegate. Check the RssParser class for the use of a delegate or implement one yourself. Set the delegate of rssParser to self in MyViewController. Now you can implement the method that is called after the document is parse with MyViewController. If you create an ivar for activityIndicator then you can easily access activityIndicator again.

Related

Creating/Invoking Objective-C Delegate Methods Objective-C

I have tried going through this Question: How Does A Delegate Work and I still don't seem to have a full grasp on it. I am trying to use the CocoaAsyncSocket library to create a TCP socket connection. Thanks to help from a very friendly SO user, I have the following code to perform a read data request to the server:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
if(msg)
{
NSLog(#"RX:%#",msg);
}
}
Now, forgive my ignorance as I'm pretty new to iOS development. So now I have this method that I want to invoke which will perform my ReadData. The problem is, I do not know WHERE to put this method (I have several views, with several header/implementation files). I want this method to be a delegate method, but I do not know how to make it a delegate method. I want to invoke this delegate method from my view.
If anyone could explain:
Where do I put this code? (What file, etc)
How do I make this a delegate method?
How do I invoke this delegate method?
I've been stuck on this all day, and I'm about to throw it the towel lol. Any and all help is much appreciated. Thanks so much!
EDIT:
This is kind of a bridge from a previous question, but I don't think that question has too much relevance to this question. Question
Thanks for updating. It is now clearer.
Here are some answers. If it is not clear, please let me know.
- Where do I put this code? (What file, etc)
This is a delegate method of CocoaAsyncSocket. Back to your first question, when you initialized it, you set yourself (your appDelegate) as the delegate.
socket = [[AsyncSocket alloc] initWithDelegate:self];
That means, you will be called from another class. So that means this method should be in the same class where you initialized the object (here socket) and set it as the delegate. So it stays in appDelegate
- How do I make this a delegate method?
You don't. This is a delegate method itself.
- How do I invoke this delegate method?
You don't. Another class (here AsyncSocket) will invoke it.
You may now ask, how you pass the data to your viewControllers?
That depends on your design. Once this method is called and you get notified that there is a connection, and data is being read, depending on your design, you pass the data to other view controllers. One way is by using NSNotification. e.g.
// Call this in onSocket:didReadData:withTag: instead of logging
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataIsReadNotification" object:msg]
// In one of your view controllers
// View controllers insterested in this message, register to get notified:
// add to -viewDidLoad
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateViewWithNotification:) name:#"DataIsReadNotification" object:nil];
...
// and somewhere in the view controller class implement this
- (void)updateViewWithNotification:(NSNotification *)notification {
NSString *msg = [notification object];
}
To make a delegate you would declare that stuff in the header file. Example below.
//SomeClass.h
#protocol SomeClassDelegate <NSObject>
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
#end
#interface SomeClass : NSObject {
id<SomeClassDelegate>delegate;
}
#property(nonatomic,assign)id<SomeClassDelegate>delegate;
#end
Now when you initialize SomeClass, you can set the delegate to whatever self is. Now In your SomeClass file, you can do
[self.delegate onSocket: .. didReadData: .. withTag:];
If the method exists in the class you set as the delegate to SomeClass, it will call it. Hope this helps.
I am not familiar with the - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
method but using delegate in iOS is very common way to have your objects communicate between each others without introducing some dependencies.
Here are few hints to answer your 3 bullets questions:
Where do I put this code?
The best bet will be within the controller you want your ReadData to be performed or in the appDelegate
How do I make this a delegate method?
If this method has been defined in a protocol this is already by definition a delegate method. You just need to have your viewcontroller class to conform to this protocol and make your controller the delegate for this method.
How do I invoke this delegate method
You don't invoke a delegate method. You have an other object that belongs to this CocoaAsyncSocket library which will make a call to this delegate method. You simply take care of handling some code within the delegate method for your controller because you have defined it as the object who will handle this method.
Just see a delegate as a way to defer some work to an other object (it is a design pattern btw)

Getting another controllers instance class

I have learned how to pass and instance of a class to another class, but I have yet to understand how to do it the other way arround.
Example:
Lets say that we are in rootViewController and in the didSelectRowAtIndexPath we pass the instance of playlist to someviewcontroller like this:
SomeViewController *someViewController = [[SomeViewController alloc] initWithNibName:nil bundle:nil];
playlist = [arr objectAtIndex:indexPath.row];
someViewController.playlist = playlist;
Now this works perfectly for passing the class instance forward, but lets instead say that Im in the someViewController and want rootViewController playlist.
How would I go about achieving this using a similar technique? Thanks for helping!
You should check out the delegation pattern: https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
Also check out Apple's CoreDataRecipes sample code:
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Listings/ReadMe_txt.html
You can see in the sample code that the RecipeListTableViewController is a delegate of the RecipeAddViewController so that when the RecipeAddViewController is done RecipeListTableViewController can show the recipe in the RecipeDetailViewController.
Delegation also helps with your object graph because the reference to the delegate is a weak link which means that it won't retain your delegate. So by using the delegation pattern you ensure that you don't create any retain cycles and ARC will take care of memory management for you.

iphone - How to send a message from one object to another?

I'm kind of a newbie to objective programming, so sorry for the dumb question.
I'm doing some kind of a messenger for some social network, and i'm stuck at the very simple thing - how to send a message from the object of one class to the object of the another class?
I have a class, called SignInViewController, which creates an instance of SignUpViewController after a SignUpButton is pressed and it's done just like that:
SignUpViewController *signUpViewController = [[SignUpViewController alloc]init];
signUpViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:signUpViewController animated:YES];
Then, after some management done with the AFNetwork (i use a specific class for this, called ServerManager), i want to send the message to draw a new textfield in my SignUpViewController instance, and i think it could be done like that:
in the SignUpViewController.h:
- (void)showTheCodeTextField;
in the ServerManager.m:
[[SignUpViewController self] showTheCodeTextField];
and then in SignUpViewController.m:
-(void)showTheCodeTextField
{
NSLog(#"Time to draw codeTextField");
}
I am getting a familiar SIGABRT at the latter line of code. I know I am doing something wrong, but I just can't figure out what exactly.
Could you help me out please?
You can use NSNotificationCenter to make that work.
After presenting your SignUpController you add it as an observer of notifications that ServerManager sends.And when that notification comes, you call your message to draw the view.
So, after
SignUpViewController *signUpViewController = [[SignUpViewController alloc]init];
signUpViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:signUpViewController animated:YES];
Make an observer
[[NSNotificationCenter defaultCenter]signUpViewController selector:#selector(showTheCodeTextField:) name:#"SignUpSucceded" object:[ServerManager sharedInstance]];
Than, in your Server Manager, you send a notification, containig that textField, and the method in your SignUp gets called.That's it.You've got the textField text in your notification.userinfo
[[NSNotificationCenter defaultCenter]postNotificationName:#"SignUpSucceded" object:self userInfo:[NSDictionary dictionaryWithObject:textField.text forKey:#"login"]];
}
Getting tour Text here in signUpView
-(void)showTheCodeTextField:(NSNotification*)notification
{
NSLog(#"Time to draw codeTextField %#",[notification userInfo]);
}
it is better if you use delegate on button event declare your delegate where you create your button and just use that delegate in any class where ever you want just use that delegate it is very helpful learn delegate for objective c its easy and efficient
Your ServerManager class needs to have a reference to the SignUpViewController instance. Right now you are getting the self of the class not of the class instance. You should probably have a property on your ServerManager class which references the instance of your SignUpViewController.
Looks like you're trying to send a message to a class (SignUpViewController) instead to it's instance/the object (signUpViewController):
Change
[[SignUpViewController self] showTheCodeTextField];
to
[[signUpViewController self] showTheCodeTextField];
and you should be o.k.
If you have an instance of SignUpViewController in your ServerManager instance, you'd use this convention (assuming your instance is named 'signUpController'):
[[self signUpController] showTheCodeTextField];
Another possibility would be to send your ServerManager a notification containing a reference to your SignUpViewController, and passing the showTheCodeTextField message to that. This assumes you have knowledge of how to send a notification.
Good luck to you in your endeavors. You look like you're just getting started in Cocoa and Objective-C. Hang in there!

can't able to handle UIActions from subclasses

I am creating a class for soap webservice to get some information from .net webserver.
for that i am using NSMutableURLRequest and parse the result using NSXmlParser.
Now i am calling that web services class from myviewcontroller.m class like this.
mywebserviceClass *obj=[[mywebserviceClass alloc] init];
[obj mymethod];
i am adding result to an array to use that array details in myviewcontroller.m class.
but i did n't get details into array while i am using that array immediately after this method.
i am trying like this by calling another method after 2 sec to use that array like this.
[self performSelector:#selector(myanotherMethod) withObject:nil afterDelay:2];
i know the reason why it is, it takes time to parse.
I am trying another way like i am creating object for viewcontroller and call this method like this.
myviewcontroller *obj=[[myviewcontroller alloc] init];
[obj myanothermethod];
NOw i can able to get details but i can't able to handle UIActivities like raising alerts.
While as told in the above performSelector method i can able to handle all UIActivities.
But i need to call that method after completion of parsing of result.
Can any one please help me.
Thank you.
Call your web service in viewDidLoad/ViewWillAppear methods of UIViewController. Collect your data and then call Parse method like- [self performSelectorOnMainThread:#selector(methodName) waitUntilDone:YES]; Below that use your array to display view or if you are displaying in table view then just call [mytblviewobj reloadData];

Cleaning up hanging NSURLConnections when a controller is dealloc'ed

I have a controller that makes HTTP GET requests using a custom class, which acts as the delegate for the NSURLConnection. Once the NSURLConnection fails or finishes, the custom class invokes methods on the controller and passes along an NSData object of received data.
I'm running into a problem where the controller in action is being dynamically created and pushed onto a navigation controller's stack. This controller makes the HTTP GET request in its viewDidLoad method. If the user quickly presses "back" on the navigation bar, this controller gets dealloc'ed. If this happens before the HTTP GET request finishes, the resulting NSURLConnection callback becomes a method call to a dealloc'ed object, which results in an EXC_BAD_ACCESS.
What's the best approach to cleaning up any pending NSURLConnections that have been kicked off by a controller which may actually be deallocated already?
I threw in some NSLog statements, and it seems that my custom class used as a NSURLConnection delegate doesn't actually receive a dealloc message. I made sure to set the controller's instance of this class to nil in viewDidUnload, and also call release on it, but it still seems to live longer than the controller.
If I understand it correctly you just need to do [whateverConnection cancel] in your viewDidUnload or dealloc method. This cancels the connection. It is almost the same if you have a custom downloader object for example for some large image that uses NSURLConnection. Make a cancel method for your class (that cancels the connection and releases it) and invoke it in your controller's dealloc method. You should also use a bool flag something like wasCanceled and do not invoke any method from your custom object's delegate if wasCanceled was set from your cancel method. (You only have a weak pointer to your delegate so it is probably released already when some other object invokes your cancel method). I assume that the delegate for your custom object is the view controller. I had several downloaders like this and it worked ok, without leaks even if I quickly canceled a download.
#interface CompaniesDownloader : NSObject /*<NSXMLParserDelegate>*/
{
id<CompaniesDownloaderDelegate> delegate; //a view controller is the delegate
NSMutableArray *companies;
BOOL isWorking;
BOOL wasCanceled;
#private
//url connection object
NSURLConnection *companiesConnection;
//this is where i put the binary data that gets transformed into xml
NSMutableData *webData;
//temporary string used when parsing xml
NSMutableString *tmpString;
//temporary company used when parsing xml
Company *tmpCompany;
}
In the implementation:
-(void) cancel
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: FALSE];
wasCanceled = TRUE;
[companiesConnection cancel];
[webData release];
webData = nil;
self.companiesConnection = nil; //OR [companiesConnection release]; companiesConnection=nil;
isWorking = FALSE;
}
You need to retain your view controller when you make a request and release when the get request finished
YourViewController.m
- (void)callGetRequest {
[self retain];
}
- (void)didFinishAllGetTask {
[self release];
}
If the user quickly presses "back" on the navigation bar, this controller gets dealloc'ed. If this happens before the HTTP GET request finishes, the resulting NSURLConnection callback becomes a method call to a dealloc'ed object, which results in an EXC_BAD_ACCESS.
When the user hits the back button, de-register the view controller class from being the delegate for that object. (Keep a reference to the object in the view controller class, so you can do something like someObject.delegate = nil;). You can do that in the view controller's dealloc method.