iPhone Programming - Passing NSMutableArray between view controllers - iphone

I have a RootViewController and a DetailViewController where I am trying to pass a NSMutableArray from the rootView to the detailView. I have created an object of the RootViewController inside the DetailViewController and accessing it like the following
RootViewController *root = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
detailViewArray = [root.rootViewArray copy];
Note: Both the arrays are declared in the .h files; synthesized and then allocated and initialized array = [[NSMutableArray alloc] init];
Problem: I am not too sure why it still doesn't work. I have tried a lot of the solutions on the internet but it didn't quite work for me. The property for the root array is nonatomic, retain Is it something wrong with that? Do I need to change it to something or the method I am following is just not right.. Please if someone could help!

If you alloc/init the RootViewController inside the DetailViewController, you are creating another instance of the RootViewController. You are not getting the same instance (with data) of the rootViewController.
That said, even passing a reference of a viewController to another viewController to then poke at it's data is sort of bad. It creates tight coupling between the views.
Instead, if you need to get data, consider using a delegate to communicate between the views.
What exactly does delegate do in xcode ios project?
Tutorial:
http://www.theappcodeblog.com/2011/04/15/passing-data-between-views-tutorial-using-a-protocol-delegate-in-your-iphone-app/
Another option is to create a shared model (read up on model view controller patterns). It's typical in that pattern to create a model and share data by getting a singleton instance of your model:
MyModel *model = [MyModel sharedInstance];
Then, each view can set and read data from that same (singleton) instance of the model.
Which to choose? The model is better if many views share the same data. A delegate is appropriate for a couple views to communicate with each via callbacks.

Even there is an another way to pass array.
MyViewController mvc = [[MYViewController alloc] initWithNibName:#"MYViewController" bundle:[NSBundle mainBundler]];
[mvc getDataArray:<pass your array>];
[self.navigationController pushViewController:mvc animated:YES];
Here it will set a dataArray property first then it will push the view controller to MyViewController class.
This will be useful when you are very much concerning about memory. Because if you are using static (sharedInstance) then its scope persistance within the entire application scope.

Related

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.

UIViewController ( init and initWithNibName )

Wish to know more about the practical differences between init and initWithNibName. SO answers such as this suggests that it is better to call "initWithNibName" indirectly through "init".
Are there any circumstances that we need to define "init" and
"initWithNibName" differently ?
Is it possible that any Nib file needs to be loaded more than once
during a single program execution ?
Are questions 1 & 2 inter-related ?
It is not better to call initWithNibName: indirectly through init. You just want to call initWithNibName: at some point. You can do that externally or internally. Some people think it's better to do it internally. I actually have a class called "LayoutUtil" that I keep layout-related helper methods to avoid writing tedious piece of layout-related code over and over. Here is my code to load a UIViewController:
+ (id)loadController:(Class)classType {
NSString *className = NSStringFromClass(classType);
UIViewController *controller = [[classType alloc] initWithNibName:className bundle:nil];
return controller;
}
And then I can just do:
MyViewController *c = [LayoutUtil loadController:[MyViewController class]];
If you want, you could add a method called ughhhh to a class and call it in there, it doesn't matter at all. The point is that it is not a better practice to call initWithNibName in the init method though, you just want to make sure you call it at some point when initiating a UIViewController.
- (id)ughhhh
{
self = [super initWithNibName:#"Myview" bundle:nil];
if (self != nil)
{
}
return self;
}
A nib file can definitely need to be loaded more than once. Everytime you call initWithNibName on a UIViewController the xib has to be loaded. A lot of people load UIViews that are not owned by a UIViewController like this:
[[NSBundle mainBundle] loadNibNamed:#"nameOfXIBFile" owner:self options:nil];
Everytime you call this function you will be loading the nib.
There are certain cases where a nib can be cached. An example would be a UITableView -- but the table view implements it's own cache. The operating system isn't doing any caching automatically.
init and initWithNibName: are related in that initWithNibName: automatically calls init on an object.
It's not ‘better to call "initWithNibName" indirectly through "init"’. You should use whichever one suits your needs better. Saying [[UIViewController alloc] init] is exactly like saying [[UIViewController alloc] initWithNibName:nil bundle:nil], so if those are the arguments you want to pass, you might as well use [[UIViewController alloc] init].
In answer to your questions:
You can define init and initWithNibName:bundle: differently if you want to. You can define just one of them. For example, UIImagePickerController only defines init, and if you try sending it initWithNibName:bundle:, it won't work properly. Or you can define some entirely different init... method. For example, UINavigationController only defines initWithRootViewController:. Whatever init methods you do define must eventually call one of its superclass's init methods.
Yes, a nib can be loaded multiple times. If you create multiple instances of the same view controller subclass, it's likely that you will load the same nib multiple times. In fact, it's possible for a single instance of a view controller to load its nib multiple times. How? If a view controller's view is not currently on screen, and the system runs low on memory, the system will ask the view controller to release its view. If the view controller later needs to put its view back on the screen, it will load the nib again.
Questions 1 and 2 are not related.
Answer of first question
Ideally every initWithNibName calls init internally so you do not need to define both in normal case Scenario , but you can if View is also loaded from loadView in one case and fron Nib as well in another case from different Views.
Answer of Second question
Everytime View is pushed in Stack Nib is loaded
Answer of Third question
No

Some memory management understanding

I have created a property of a viewController and retaining it from ClassB of viewController (Class A).
so basically I have #property (nonatomic, retain) ClassAViewControllerVC, and synthesized in the main file.
I have an IBAction in which I am allocating ClassAViewController and pushing it on navigation stack, but I am trying to analyze where should I release this viewController?
- (IBAction) response {
ClassAViewControllerVC = [ClassAViewController alloc] initWithNib:#"ClassAViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:self.ClassAViewControllerVC animated:YES]
}
Is it okay to release the view controller after I stack it on the navigation-Controller as described above?
Also, is it a good idea to set property for such viewController at the first place? I started to notice that my apps started to crash if not utilized #property retain way. Any thoughts or concern would be appreciated.
Thanks
Firstly, no, it's not necessary to keep such an object in a property. You only need to keep objects in a property if the class will require access to the object later on. In this case, I think a local variable will do.
In this example, you create a ClassAViewController with alloc, meaning that the caller (this method) has responsibility to release it once it's finished with it.
When you add it to the navigation controller stack, the navigation controller retains it, because it keeps a reference to it.
So, at the end of this method, you should release it, but it's been retained by the navigation controller, so it's not deleted.
The code should look like this:
- (IBAction) response {
ClassAViewController *viewController = [ClassAViewController alloc] initWithNib:#"ClassAViewController"
bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:viewController animated:YES]
[viewController release];
}
P.S. it's convention in objective-C to write variable names starting with a lowercase letter. Uppercase starting letters are used for class names, and it confuses the bejeesus out of me! ;)
if ClassAViewControllerVC is a field of ClassB, you should release it in the dealloc method.
But you should probably create ClassAViewControllerVC in the init or even better viewDidLoad method.(note: if you create something in viewDidLoad, you need to release it viewDidUnload as well, not only in dealloc)
Because now every time "response" method is called ClassAViewControllerVC will be created all over again. (and there is not much sense storing it in the property). If this is what you want you probably should not make ClassAViewControllerVC a filed of your ClassB, but just have it as s local variable in response method like this:
- (IBAction) response {
ClassAViewController *classAViewController = [ClassAViewController alloc] initWithNib:#"ClassAViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:classAViewController animated:YES];
[classAViewController release];
}
Best way to solve this problem is, drag and drop empty UIViewController in your ClassB.xib file. Create #property (nonatomic, retain) ClassAViewControllerVC, make the connection to empty UIViewController you just drop to classB.xib file and also change the NIB name to the class A nib file name. This will solve all the problem of crashing because you are not allocating or releasing any memory.

Loading/initializing secondary UITabBarController from/with nib

In the application I'm trying to develop I have UINavigationController as a root controller. I initialize views using pretty common code:
MySubclassOfViewController *vc = [[MySubclassOfViewController alloc]
initWithNibName:#"MySubclassOfViewController"
bundle:nil];
vc.title = #"A title";
[self.navigationController pushViewController:vc animated:YES];
[vc release];
After a succession of some views I want to load UITabBarController.
Is there a way to desing the nib file and create an instance of UITabBarController the same way as above?
I know I can do this programmatically or by explicitly declaring an outlet and connecting it with the controller in the nib. It's also possible to initialize the controller using something like
NSArray *objects = [[NSBundle mainBundle]
loadNibNamed:#"MySubclassOfViewController"
owner:self
options:nil];
self = [objects objectAtIndex:0];
[objects release];
But can I make it without extra work & typing?
Let's say I define a subclass of UITabBarConroller (although I know it's discouraged in the Apple docs, but just out of curiosity). When I make an instance of the subclass, can I somehow load the superclass part out of a nib?
I've solved my problem myself. After some research, I came to these conclusions.
It's not possible to do it the same way (i.e. UITabBarController *tbc = [[UITabBarController alloc] initWithNib...) Either there should be an extra outlet for the controller, or the controller should be explicitly alloc/init'ed (but not initWithNibName), or it could be instantiated using loadNibNamed. That's it. The reason for this is that the tab bar controller doesn't have outlets declared to connect them to children view controller. (For more details on loading nibs check Development Chaos Theory blog).
If the previous answer was 'yes', then all I had to do was to set the custom class in the Interface Builder to my subclass. This works for other objects (well, may be except the UINavigationController, which is not intended for subclassing as well). But since the 'simple' initialization of a tab bar controller is impossible, this is impossible too.
Any comments on this? :)

Adding a variable to all UIViewControllers

I have this class (let's call it FooViewController) that's a subclass of UIViewController. It's supposed to act similarly to a UINavigationController, in that there's a rootController and you can add other UIViewControllers to it. And each UIViewController within FooViewController can create another UIViewController and push that new UIViewController to FooViewController.
Here's an example of what I mean. This is the auto-created code when you add a new UITableViewController to your project.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"Nib name" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
Here's the question: How can I have a variable like navigationController in all of my UIViewControllers? Just like you can add methods to existing classes using categories, can you also add variables to existing classes? It sounds like Associative References is what I'm looking for, but it's only available on a Mac.
One solution that would work for me is to subclass all the UIViewControllers that I might use, and have my actual classes subclass off of those. For example, I might do:
#interface FooUIViewController : UIViewController {
FooViewController *fooViewController;
}
#interface FooUITableViewController : UITableViewController {
FooViewController *fooViewController;
}
So that I could do: [self.fooViewController pushViewController:detailViewController];. But this seems like a dirty way of doing it.
I feel this shouldn't be a difficult thing to do and maybe I'm thinking about it wrong. Any thoughts? Thanks!
No, you can't add an instance variable to UIViewController.
Subclassing is not a bad way of doing it, considering more things it can do in relation to FooViewController. It doesn't harm the design of your classes because they depend on FooViewController anyway (just as the same as UIViewController having navigationController property).
Another way of doing it is to access the FooViewController object via the application delegate. But I think this is a dirty way of doing it (because they now depend on your application delegate).