Access NSMutableArray from another class - Objective C - ios

I have a main ViewController that contains a desginated class. Within that ViewController there is a Container that is linked to an embed ViewController. Within that embed ViewController I am creating an NSMutableArray. I am not trying to access that array inside the main ViewController. I know that if I use:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
NSLog(#"%#",myScript.selectedCells);
The NSLog will output null because I am creating a new ViewController and that gets rid of the already set array. So my question is how can I access that array without overwriting it?
UPDATE:
Heres where the NSMutableArray is being created:
create_challenge_peopleSelect.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
create_challenge_peopleSelect.m:
if([selectedCells containsObject:label.text])
{
cell.accessoryType = UITableViewCellAccessoryNone;
[selectedCells removeObjectIdenticalTo:label.text];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[selectedCells addObject:label.text];
}
This class is the container class off the main ViewController
No I want to access the selectedCells within my main ViewController, I have been doing things such as:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
I would prefer to stay away from the App Delegate If possible.

You seem to be unclear on the difference between classes and instances. OK, so, say we have two NSArrays:
NSArray *a = [[NSArray alloc] initWithObjects:#"hello", #"I", #"am", #"an", #"array", nil];
NSArray *b = [[NSArray alloc] initWithObjects:#"so", #"am", #"I", nil];
If I do a.count, I'll get 5 as the answer because the array contains five objects. Meanwhile, if I do b.count, I'll get 3, because that array contains three objects. It isn't that creating b "gets rid of the already set count". They are separate objects completely unrelated to each other.
Your view controller class is the same way. When you create a different instance, it doesn't overwrite the old one -- it's just not the same object. In order to use the original view controller object, you need to get a reference to it.
So how do you get a reference to it? Well, the general answer is you design your app so that the two objects know about each other. There are lots of specific ways to accomplish this. A lot of people will say "Just stick a reference in the app delegate." That is one thing you can do, but it's not always the best choice. It can get out of control if you just stick everything in your app delegate. Sometimes it's the right answer, often other things are the right answer. Another approach is to have an object that knows about both of those objects introduce them to each other. But sometimes there is no such object. So it's situational.

Basically, instead of creating a new view controller, you need to maintain a pointer to the original.
I suggest storing an instance of your UIViewController in the AppDelegate in order to retain the particular instance of the view controller you've created by making it a global variable.
ex. In the App Delegate.h
#import "ViewController.h"
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic) ViewController *viewController;
Then from whatever view controllers' .m's from which you need to read/write to the variable, create a pointer to the application's app delegate, ex:
#import "AppDelegate.h"
#interface WhateverViewController ()
AppDelegate *mainDelegate;
- (void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
}
So wherever you first create that view controller in your code (before ever using it), initialize it using this global variable. ex. If you're using xibs:
mainDelegate.viewController = [[UIViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
ex. If you're using storyboards:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
mainDelegate.viewController = [storyboard instantiateViewControllerWithIdentifier:#"viewControllerID"];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
(This is assuming it's in a place other than the app delegate in which case the pointer to the App Delegate isn't needed.)
Then when accessing the array from another UIViewController use
mainDelegate.viewController.array

To access the NSMutableArray from one class to another class use following code.
In the first view controller in which u have declared the object of NSMutableArray, declare the property and synthesize for the same as below,
//In FirstViewcontroller.h class,
#property (nonatomic, strong) NSMutableArray *arrData;
//In FirstViewcontroller.m class
#synthesize arrData;
Also FirstViewcontroller object should be global so you can create the object of FirstViewcontroller in app delegate file.
//appdelegate.h
#property (nonatomic, strong) FirstViewcontroller *objFirst;
//appdelegate.m
#synthesize objFirst;
FirstViewcontroller *objFirst=[[FirstViewcontroller alloc]init];
Now in SecondViewcontroller in which you have to access array,
create the share object of Appdelegate file
//SecondViewcontroller.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then use will get the required array as below,
app.objFirst.arrData
This is your required array I hope it will help you.

The basic idea here is that in your original class, the array is referred to by a pointer. Your original class would allocate it and presumably load it. Other parts of your program can be handed the contents of the property, which is a pointer, assign that to their own pointer holder, and use it as if you had declared it there. Please use the above code;
MyClass *aClass = [[MyClass alloc] initWithMyInitStuff];
NSMutableArray *ThatArray = aClass.MyArray;
NSLog("Count of ThatArray: %d", [That.Array count]);

What you've done in the code provided is set a public property for a mutable array...
#property (strong, nonatomic) NSMutableArray *selectedCells;
The NSMutableArray is not "created" by setting that property. At some point in your code you also have to create the NSMutableArray by initialising...
NSMutableArray *selectedCells = [[NSMutableArray alloc] init];
or by using a convenience method such as...
NSMutableArray *selectedCells = [NSMutableArray arrayWithCapacity:(NSUInteger)<initialising capacity>];
or
NSMutableArray *selectedCells = [NSMutableArray arrayWithArray:(NSArray *)<initialising array>];
Initialising an NSMutableArray is often done only once. If it is repeated, the contents are overwritten against the property used to point to the array. As such, a useful location for this is often within the viewDidLoad view controller lifecycle method.

Related

addObject to NSMutableArray is nil even after initialization?

I have an NSMutableArray declared as property in .h and initialized in viewDidLoad in my SPOCVC .m (UIViewController)...
#property (strong, nonatomic) NSMutableArray* SPOCTrackList;
in viewDidLoad
if ([self SPOCTrackList] == nil) {
self.SPOCTrackList = [[NSMutableArray alloc]init];
NSLog(#"SPOTTrackList INITIALIZED");
}
In a separate VC, I'm trying to pass/addObject to SPOCTracklist...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SCTrack* selectedTrack = self.trackList[indexPath.row];
[[[SPOCVC sharedInstance]SPOCTrackList]addObject:selectedTrack];
NSLog(#"%lu", (unsigned long)[[[SPOCVC sharedInstance]SPOCTrackList]count]);
So my NSMutableArray is initialized and I can add dummy objects, but why can't I pass it from another VC using singleton or anything, such as...
SPOCVC* spocVC = self.tabBarController.viewControllers[2];
[spocVC.SPOCTrackList addObject:selectedTrack];
Thanks for pointing me in the right direction.
View controllers are only intended to be around while they are on screen. They are not a place to store data. Generally when one view controller talks directly to another view controller that it didn't create, you're doing something wrong.
Move SPOCTrackList to your model and have both view controllers talk to it rather than to each other.
There should never be a "sharedInstance" on a view controller. That's a sure sign that you're abusing the view controller as the model.
What's probably happening in your particular case is that viewDidLoad is running on a completely different SPOCVC than your sharedInstance.
why not use appdelegate to handle this
appdelegate.h
//add property to hold the reference
#property (nonatomic, copy) NSMutableArray *referenceArray;
//share the app delegate
+(AppDelegate *)sharedAppDelegate;
#end
in appdelegate.m
//synthesize the property
#synthesize referenceArray;
//return the actual delegate
+(AppDelegate *)sharedAppDelegate {return (AppDelegate *)[UIApplication sharedApplication].delegate;}
in viewdidload method
//add the delegate
import "appdelegate.h"
//init the array
self.SPOCTrackList = [[NSMutableArray alloc]init];
//Add reference
[AppDelegate sharedAppDelegate].referenceArray = self.SPOCTrackList;
and add anywhere like this
import "appdelegate.h"
[[AppDelegate sharedAppDelegate].referenceArray addobject:object];

Pointer to NSMutableArray returns Null

I am trying to use a NSMutableArray that I have created in ViewController in an ViewController2. But it is just returning nil.
Here is my ViewController.h file:
#property (nonatomic, strong) NSMutableArray *total_hours;
Here is my ViewController.m file:
total_hours = [[NSMutableArray alloc] init];
I also add object. Use NSLog to display that this have actually been added, so that is working. But now I try to use NSLog to display them again in the other ViewController2.
Here is my ViewController2.h file:
#property(nonatomic, assign)NSMutableArray*total_hours_copy;
here is my ViewController2.m file:
#import "TimelisteViewController.h"
// some auto enabled code
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
TimelisteViewController *test = [[TimelisteViewController alloc]init];
NSMutableArray *total_hours_copy = test.total_hours;
NSLog(#"%#", [total_hours_copy objectAtIndex:0]);
}
Why is this not working?
Your question implies that you create the array in ViewController and want to later pass it to ViewController2. However, in ViewController2's viewDidLoad method, you create a new instance of ViewController. So that's a problem.
It would be easier to answer your question if :
You indicated in which method total_hours is initialized.
How control is transferred between the 2 controllers.
You are not initializing you array in the right place. viewDidLoad is only called when the view controller is shown, not at the initialization.
You could override init method in your view controller :
- (void)init{
[super init];
total_hours = [[NSMutableArray alloc] init];
}
However this is not a usual pattern, and i won't recommend it. I'm not sure what you're trying to do, but i believe it would be best to initialize you array in your viewcontroller2 and pass it after to your newly initialize controller.
TimelisteViewController *test = [[TimelisteViewController alloc]init];
NSMutableArray *total_hours = [[NSMutableArray alloc] init];
//Add your data in the array
test.total_hours = total_hours;
Based on the fact that Paul Lalonde has made good considerations, if you need to pass the array from a view controller to another you can follow two ways.
Create a singleton class that would share the array (in this way each controller can access to that singleton and hence to that array)
Inject the array from a controller to another (preferred way since it allows decoupling components and having less application rigidity)
So, following the second solution, from ViewController1 you inject the array like the following snippet. Now both controller will share the same array. Modifications made by one controller will be visible to the other and vice versa...
ViewController2 secondController = // alloc-init here…
secondController.sharedArray = [self sharedArray];
where ViewController2 would have a property like
#property (nonatomic, strong) NSMutableArray* sharedArray;
Then, for example, within its viewDidLoad or wherever you want you can say
[self.sharedController add…]
Said this, what it your application flow? For example, is ViewController2 a controller that is displayed after ViewController1 through a UINavigationController or something similar?
total_hours_copy is a local variable in your viewDidLoad method, not a property!. Change your code to
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
TimelisteViewController *test = [[TimelisteViewController alloc]init];
self.total_hours_copy = test.total_hours;
// or maybe self.total_hours_copy = [test.total_hours copy];
NSLog(#"%#", [total_hours_copy objectAtIndex:0]);
}

Best way to safely clear data in a singleton?

I'm using a singleton called CSAppData to store data for my iPhone app. I'm storing an object called CSInbox in the singleton. When I logout of my app, I want to clear the data for that object.
Here is my singleton code, including the method for clearing the data:
- (id)init {
self = [super init];
if (self)
{
self.inbox = [[CSInbox alloc] init];
}
return self;
}
+ (CSAppData *)appData {
static CSAppData * appDataInstance;
#synchronized(self) {
if(!appDataInstance) {
appDataInstance = [[CSAppData alloc] init];
}
}
return appDataInstance;
}
+(void) clearData {
CSAppData *appData = [CSAppData appData];
appData.inbox = [[CSInbox alloc] init];
}
However, in one of my view controllers, in the initWithCoder method, I'm storing the inbox variable:
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
self.inbox = [[CSAppData appData] inbox];
}
return self;
}
So, when the app logs out and the clearData method is called, the view controller is still pointing to the old CSInbox object. And even though I am initializing a new view controller and setting it to the root view controller (in the AppDelegate), like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MainTabControllerViewController *viewController = (MainTabControllerViewController *)[storyboard instantiateViewControllerWithIdentifier:#"mainView"];
[self.window setRootViewController:viewController];
The one child view controller that has the CSInbox is never reinitialized, and is still pointing to that old CSInbox object. (I'm not sure why this is happening.)
So, what is the best way to solve this?
Change the clearData method in the singleton to just reset the properties of the CSInbox object, rather than alloc and init and new one?
Move the self.inbox = [[CSAppData alloc] init]; to the viewDidLoad in the view controller class so it gets set properly upon the second login?
Change the logout function in the AppDelegate so that the root view controller and all other view controllers are released, so they will reinitialize upon the second login?
I'm leaning toward #1 or #3...
As requested, here is CSInbox.h:
#interface CSInbox : NSObject
#property (nonatomic,strong) NSMutableArray *threads;
#property (nonatomic, assign) NSInteger newCount;
#property (nonatomic,strong) NSDate *lastUpdate;
-(void) setThreadsFromJSON:(NSDictionary *)json;
#end
And here is CSInboxViewController.h:
#interface CSInboxViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, CSThreadViewControllerDelegate>
#property (strong, nonatomic) IBOutlet UITableView *inboxTableView;
#property (strong,nonatomic) CSInbox *inbox;
#end
And CSAppData.h:
#interface CSAppData : NSObject {
CSInbox *inbox;
}
#property(nonatomic,strong) CSInbox *inbox;
+ (CSAppData *)appData;
+ (void)clearData;
#end
I think the answer lies not in destroying the singleton object and recreating it, but to actually clear the instance variables within that singleton object.
You don't show the declaration of [CSAppData inbox], but if it's an NSMutableArray, for example, then you can clear that, and any existing references to the singleton object can remain:
+(void) clearData {
CSAppData *appData = [CSAppData appData];
[appData.inbox removeAllObjects];
}
One way to handle this, complying with the spirit of using a singleton, is having your view controllers access directly your singleton inbox, i.e.: [CSAppData appData].inbox instead of self.inbox. This is a bit wordier, but it would "magically" fix your issue.
If that is not acceptable to you, I would go with option #1 of those you list. Even better, I would make the inbox in the singleton a singleton itself, or make sure it is never replaced by another instance.
EDIT:
Another approach you have, is using KVO in your controller so that it gets notified when the inbox object has changed. Don't know if it is quite worth it, but could be used.

Using mutable arrays in other classes

I've tried several ways that i've found here but none have worked. what would be an easy way to pass this NSMutalbeArray into another View controller?
NSMutableArray *pph = [[NSMutableArray alloc] init];
[pph addObject:[NSString stringWithFormat:#"%d %d %d",diario.Cowid,diario.Lact,diario.Del]];
on the same file below i have
- (IBAction)masInfoPPH;
{
tipo = #"PPH";
adiario = pph;
NSLog(#"\n Array adiario: %#",pph);
DetDiarioViewController *DetDiarios = [[DetDiarioViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:DetDiarios animated:YES];
}
for some reason pph (the NSMutalbeArray) gets here as null but up there it does give me the objects it should have. adiario is a global array or at least its supposed to be. Help!
There really are no global arrays. Create a property in your class for pph in the interface of your class.
#property(nonatomic, strong) NSMutableArray *pph;
self.pph = [[NSMutableArray alloc] init];
[self.pph addObject:[NSString stringWithFormat:#"%d %d %d",diario.Cowid,diario.Lact,diario.Del]]
But you still need to get that into next view controller. Create a similar property in it's interface and then set it before pushing
DetDiarioViewController *detDiarios = [[DetDiarioViewController alloc] initWithNibName:nil bundle:nil];
detDiarios.pph = self.pph;
[self.navigationController pushViewController:detDiarios animated:YES];
BTW - in objective-c the convention is to use a lowercase letter for the first letter of an instance
The scope of your pph array is unclear from your description.. But ANYTHING declared inside a single method is LOCAL to that method, unless it is returned BY that method.
You have several options... Declare the array as an instance variable, ie..
#interface YourClass: NSObject { NSMutableArray *pph; }
or
#implementation YourClass { NSMutableArray *pph; }
or as a static variable (in your .m file) (which would enable you to access the value from Class (+) methods..
static NSMutableArray *pph = nil;
or most preferably... as a property
#interface YourClass #property (strong) NSMutableArray *pph;
which you can then call upon from any instance method via the automatically synthesized Ivar _pph, or via the property's accessors.. self.pph.
Hope this helps!

Access the value of text field from another class

I am making an app using a utility application template. I am trying to access the value of a UITextField from the FlipSideVewController class.
In the MainViewController.h file I have -
#interface MainViewController : UIViewController <UISplitViewControllerDelegate>{
UITextField *textField;
NSString *myText;
}
#property (retain, nonatomic) IBOutlet UITextField *textField;
#property (nonatomic, retain) NSString *myText;
-(IBAction)pressButton:(id)sender;
In the MainViewController.m file -
myText = [[NSString alloc] initWithFormat: textField.text];
NSLog(#"%#",myText);
I am creating the FlipSideViewController in the MainViewController class using the following code -
FlipsideViewController *controller = [[[FlipsideViewController alloc] initWithNibName:#"FlipsideViewController" bundle:nil] autorelease];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
This prints the value of the textfield in the console without any problems. The problem happens when I try to access the value of the textfield in the FlipSideVewController class (after the user presses the go button).
In the FlipViewController class I have -
MainViewController *obj = [[MainViewController alloc] init ];
NSString *abc = obj.textField.text;
NSLog(#"%#",abc);
The FlipSideVewController nib file is loaded fine without any problems. However the console output is (null) when in FlipSideVewController.
I will appreciate any help.
Thanks
If you use the Utility Xcode template, you should think of MainViewController and FlipSideVewController as given: the framework will instantiate them for you and make it available to the rest of the program as you define (either in your code or in IB). What I mean by this is that your line:
MainViewController *obj = [[MainViewController alloc] init ];
does not really do what you want.
In your case, what you seems to want is access a text field controlled by the existing MainViewController instance (the one that gives you the NSLog output correctly) from your other existing FlipSideVewController instance. This is a matter of "connectin" somehow those two controllers.
There are several ways to accomplish this. One is defining a "model" for your app and have it shared between the controllers. See also the Model-View-Controller pattern. A model is just a data structure that contains your "data"; you make that data structure available to both of your controllers. The easiest way to create such data structure and have it shared is through a singleton (I am not suggesting to use it as the best way, just noting that it is the easiest way, IMO).
Another, less clean way is passing a reference to MainViewController to FlipSideVewController and then accessing the text field through it. By example, you could define an ivar in your FlipSideVewController, then, where the two controllers are created, you do the assignment to the ivar.
You should go to your MainViewController and declare your textField as a property first and synthesize it, so you can access it using obj.textField. And if you have just created obj using alloc and init, you wont have any text in the textField instance Variable.
MainViewController.h
#property (retain) UITextField *textField;
MainViewController.m
#synthesize textField;
and you could use
myText=textField.text;
Now this should do it and you can access this textField by obj.textField in your other class. But you still wont get its value if you are initializing it in your other class because you will be creating a brand new obj whose textField.text will be blank( unless you have overrided its designated initializer to set the textField.text value).
Declare NSString *abc as instance variable
NSString *abc;
and then as property
#property (copy) NSString *abc;
#synthesize abc;
After you create your FlipSideViewController,
controller.abc=myText;
Remove the code where you create obj.
This will do it.

Resources