Sharing data across ViewControllers - objective-c

After reading a bunch of stackoverflow posts and forums I got really confused on this subject. Alot speak about using delegates, but can result in memory problem if there is a big amount of data.
I am only to handle around 600-bytes of data from a TCP/IP device and override the existing value with the new.
I have made a RootViewController which has a special view and the TCP/IP socket, furthermore I have two other ViewControllers which has to use data from the RootViewController (TCP/IP).
Should I perhaps make a seperate class for the TCP stuff instead of mixing it up in my RootViewController?
Also how should I handle the sharing of data across the ViewControllers?
Thanks in advance.

If you are doing a lot with your TCP 'stuff' (for example get the data, display the data, reformat the data, compair or change the data and then re-save etc) then I would suggest creating a separate class (probably NSObject) that has all the metods to handle the TCP stuff and then create an instance of that object in your root view controller.
As for getting the data in other view controllers you would have to create a reference to your root view controller in other view controllers and then connect them in interface builder (or programmatically). Just make sure you dont create a brand new instance of root view controller (i.e. dont use [[RootViewController alloc] init];)
Does that make sense? If you post more about the structure of your app (like are you using a navigation controller etc) I could be more specific for you.
Good luck!

For sharing data among classes you can use notifications mechanism. Post a notification in your FromClass.m:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:yourTCPData, #"TCPData", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"gotTCPData" object:nil userInfo:options];
In your ToClass.m viewDidLoad add this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(makeSomeThingUsefulWithTCPData:)name:#"gotTCPData" object:nil];
...and somewhere in your ToClass.m add this method:
- (void)makeSomeThingUsefulWithTCPData:(NSNotification *)notification {
NSDictionary *dict = [notification userInfo];
yourTCPDataClass *yourTCPDataObject = [dict objectForKey:#"TCPData"];
// Here make something useful with your TCP data
}
Also add this in your ToClass.m viewDidUnload to release observer object when view will be unloaded:
[[NSNotificationCenter defaultCenter] removeObserver:self];

Related

Objective-C: Pass data between two not connected View Controllers

I have a ViewController A with a UILabel and a ViewController B with a button, so I want to update the label once I press the button. I could use delegates for this, but my ViewControllers are not connected and I can't use something like setDelegate, I don't create any instance of one in another. So basically they are created somewhere else. Is there any way to do that?
Variant 1: if ControllerA and ControllerB life-time differs
Use NSUserDefaults. On ControllerB button click store data into NSUserDefaults, in ControllerA read data from NSUserDefaults and show in label (NSUserDefaults is also observable, so can track changes in run-time)
Variant 2: if ControllerA and ControllerB both currently in run-time
Use NSNotificationCenter. On ControllerB button click post a NSNotification with data in userInfo, and ControllerA in notification handler extracts data from userInfo and assign to label.
Use Coordinator pattern.
Class that will create/get the 2 instances of both VCs,
and with delegates will move the data between them.
The advantage of Coordinator pattern is that your VCs can be re-used on others places (same or other project), and also the code is cleaner.
To expand on Apsperi's answer:
Notification method of changing the view controller:
In the view controller with the button on button press call:
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh"
object:self userInfo:nil];
Then in the viewdidload or viewdidappear method of the label view controller place the "receiver" for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mymethotochangetext) name:#"refresh" object:nil];
The NSUserDefault method:
In the view controller with the button save a NSUserDefualt:
[[NSUserDefaults standardUserDefaults] setObject:somestring
forKey:#"myuniquekeyname"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then in the view controller with the label you can get the saved string when the view loads and place it in the text field:
self.label.text=[[NSUserDefaults standardUserDefaults]
stringForKey:#"myuniquekeyname"];

NSTableView reload data from different controller

I am having problems updating the content of two tables, which are placed in two different tabs of a NSTabView and each controlled by a different controller (Controller1 and Controller3), from a third controller (Controller2).
I tried three different approaches:
Approach #1:
I created in Controller1 (the controller of the table invitesTableView) the method refreshTable :
- (void)refreshTable {
invites = //fill my mutable array
NSLog(#"%#",invites);
[self.invitesTableView reloadData];
NSLog(#"invite's table view updated");
}
which I later call from Controller2 doing :
Controller1 *controller1 = [[Controller1 alloc] init];
[controller1 refreshTable];
NSLog prints the content of the array invites correctly, but the table is not updated. I should say that, at application launch, I call the very same method in Controller1 and the content of the array invites is correctly loaded in the table.
Approach #2: When in Controller2 I do:
Controller1 *controller1 = [[Controller1 alloc] init];
controller1.invites = //fill my mutable array
NSLog(#"%#",controller1.invites);
[controller1.invitesTableView reloadData];
But again the content of the table is not updated, even if the NSLog shows the correct content.
Approach #3: As Controller1 controls the content of a NSTabViewItem, I integrated in Controller1 the method:
- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)inviteTab
and it works just fine, because I call the method refreshTable as soon as open the tab controlled by Controller1. The problem is that I have different tables in different tabs but I can set only one controller as the delegate of the NSTabView, so I can not update the table controlled by Controller3 in a different table.
I don't think there is an easy solution to approach #3, apart from merging Controller1 and Controller3, but I think I am just missing something with respect to the first two approaches.
Hope I explained myself clearly. Can anyone help? Thanks in advance!
You can use notifications/observer to reload tableview.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTable:)
name:#"reloadNotification"
object:nil];
- (void)reloadTable:(NSNotification *)notif {
[self.tableName reloadData];
}
In your first tab use this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadNotification"
object:nil];

Adding 2 observer with one name in NSNotificationCenter in 1 class

I have a observer problem with NSNotificationCenter in my app.
My AppDelegate class has 2 service class to get data by url which called ExhibitionService & NewsService.
This 2 service class uses one Queueloader class in itself.
When I wrote 2 observer to listen service load operations in my appdelegate class, it returns error and crashes.
APP DELEGATE CLASS
ExhibitionLoaderService *exhibitionService = [[ExhibitionLoaderService alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exhitibionServiceComplete :) name:**CserviceComplete** object:nil];
[exhibitionService load];
NewsLoaderService *newsService = [[NewsLoaderService alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(newsServiceComplete :) name:**CserviceComplete** object:nil];
[newsService load];
ExhibitionLoaderService.m & NewsLoaderService has the same method
-(void)load
{
Queueloader *que = [[Queueloader alloc] initWithPath:CExhibitionURLPath isVerbose:NO];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didQueComplete:) name:CdidQueueloaderComplete object:nil];
[que startOperation];
[que release];
}
ERROR I GOT
[[NSNotificationCenter defaultCenter] postNotificationName:**CdidQueueloaderComplete** object:results];
2 service class has CdidQueueloaderComplete... Problem is about observers but how? what?
PS. Program received signal EXC_BAD_ACCESS.
Thanks.
There's no problem with having multiple observers of the same notification. The problem you describe sounds a lot like it's related to the lifetime of your observers.
If you deallocate an object that's still registered to listen to notifications, the NSNotificationCenter doesn't know about that. If a notification comes in in the future, the center will forward it to the object it thinks is still listening (but that has gone away), and you get a crash.
The solution to this problem is to ensure that your object is removed as an observer before it is destroyed. There are two ways to do this:
often you'll know when an object should start or stop listening to notifications, and you can make sure you remove it as an observer when it should stop (for example, perhaps view controllers should start listening for model updates when their views appear and stop listening when their views disappear)
other times, an object can look after its own notification lifecycle: perhaps you can start listening in an initialiser and stop listening in -dealloc.
Whatever way you do it, you need to balance adding observers and removing observers so that when an object goes away it's no longer registered with the notification center.

Exchange data between un-related (table)viewcontrollers?

I have a app with TabBar, under the main tabbar there are navigation controllers, under these there are 'branches' of tableviewcontollers.
How can I make two unrelated, non segue-connected, under two different tab icons viewcontrollers exchange information between them?
Tried with delegating but I can't get the instance variable to the delegator from the delegatee (there's no relation between them, no segue.destinationviewcontroller etc)
any ideas?
practical:
the app shows list of subitems (the parent tableview has the items), andon the other tab the recent items that were selected are getting added (but maxiumum 10 and sorted by most recent).. been breaking my ** on it...
thanks
I find NSNotifications not the best way to do this since you do couple the unrelated TableViewControllers. I think the flow of information in an application is crucial.
The solution I would personally favor is having a central class, that manages the global data for your application. This can be the AppDelegate class or an arbitrary manager class that manages the flow of data and "pulls the strings" and mediates between the different independent ViewControllers.
Example:
Using this code you can get hold of your UITabBarController and set your class as the delegate of your ViewControllers etc.:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *rootTabBarController = (UITabBarController *)self.window.rootViewController;
MyFirstTableViewController *firstVC = [rootTabBarController.viewControllers objectAtIndex:0];
firstVC.delegate = self; // Get informed about events in the first ViewController
MySecondTableViewController *secondVC = [rootTabBarController.viewControllers lastObject]; // Assuming you only have two Tabs
// Once you have the rootTabBarController you can cast it to the corresponding ViewController and access any nested UIViewControllers
return YES;
}
#pragma mark - MyFirstTableViewControllerDelegate
-(void)firstTableViewController:(MyFirstTableViewController *)sender didSomethingFancy:(MyFancyObject *)fancy{
// Do stuff like fetching some data, based on the event from the first ViewController
// Maybe tell the secondVC to refresh its data etc. etc.
}
Have you considered posting an NSNotification, passing the data you want to transfer in userInfo?
From the Apple documentation:
The object sending (or posting) the notification doesn’t have to know
what those observers are. Notification is thus a powerful mechanism
for attaining coordination and cohesion in a program. It reduces the
need for strong dependencies between objects in a program (such
dependencies would reduce the reusability of those objects).
The class sending a notification does it as follows:
[[NSNotificationCenter defaultCenter] postNotificationName: <notification-name>
object: self
userInfo: <your-user-info>];
and the class listening for the notification registers for notifications using:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(someMethod:)
name: <notification-name>
object: nil];
and has a method:
- (void) someMethod: (NSNotification *) notification
{
NSDictionary * myInfo = notification.userInfo;
//do something with myInfo
}
You can use #property and #synthesize to create default getters and setters. You can aso create your own getters and setters manually.

Objective-C game development: communication between components

I am using Cocos2d game development framework for iPhone.
Let's focus on the battle scene:
The battle scene has as children: battlers layer, HUD layer, menu layer, background layer, etc...
Sometimes, it is necessary that my battlers layer "contacts" my HUD layer (like call a function within it).
I find this hard. Basically, my battlers layer needs some kind of.. instance or reference of the HUD layer in order to call a function within it, right? But I don't know how to have such thing.
Currently, this is what I do:
The battlers layer will run a function in the scene (its parent), and, inside such function, I will "locate" the HUD layer child, and call the function I need in it.
Now, that is kind of inconvenient. What would you do in such situation?
It sounds to me that you may have over-designed this. What I might do is something like this.
I'd have an IScene. Each of my scene classes implements this IScene. IScene has a property called "HUD", another called "Menu", etc.
The current IScene is set into a global static instance ::CurrentScene
When the current scene needs to contact the menu, I say: ::CurrentScene->Menu->SomeFuncion().
Would that work for you?
I'd suggest you take a look at Cocoa's NSNotificationCenter and related classes. Apple has a guide to the subject here.
It might work something like this.
In your HUD layer, you subscribe to notifications with the name #"battleLayerStuff":
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doThisWhenSomethingHappens:)
name:#"battleLayerStuff"
object:nil];
And in your battle layer, when something happens, you post a notification with the same name:
[[NSNotificationCenter defaultCenter] postNotificationName:#"battleLayerStuff"
object:battleObject];
The object part is optional, but can be helpful if you want to send more information than just "something happened".
If you want to extract information from the object you send you do this in the doThisWhenSomethingHappens: method:
- (void)doThisWhenSomethingHappens:(NSNotification *)notification
{
BattleObject *battleObject = (BattleObject *) notification.object;
// Do stuff with object
}
You could use NSNotification Center. This allows you to send messages in one object and have multiple other objects react to them.
// The object that wants to receive the message registers with NSNotificationcenter
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(receiveScoreUpdateEvent:)
name:#"scoreUpdateEvent"
object:nil ];
In the same object you need to define the method that your selector points to:
- (void)startLocating:(NSNotification *)notification {
NSNumber *scoreObject = [[notification userInfo] objectForKey:#"score"];
// Do something with the new score
}
Another object can then send a message with the updated score at any time and your HUD would react to it:
[[NSNotificationCenter defaultCenter] postNotificationName:#"scoreUpdateEvent"
object:self userInfo:[NSNumber numberWithInt:5345] forKey:#"score"]];