Adding array from one view to another and retaining values - objective-c

I have four views, with four arrays. As soon as I navigate from one view, I add that particular array to my master array.
Initially this master array has nothing, and is allocated when the app runs using 'init' method. It does not have a view and it is an array from a subclass of NSObject.
Finally the master array should consist of {viewarray1, viewarray2, viewarray3, viewarray4}.
Each array is added to the master array when navigating to the next view.
So is there anything wrong in my implementation ? Is it okay to allocate masterArray in the init method? Each time I add an object to masterArray, I NSLog it and it displays (null)
How can I have the master array retain values for the whole app??
Some Information on the Code:
I initialize my master array in another class, in the -(init) method
masterArray = [[NSMutableArray alloc] init ];
While adding an object to MasterArray from another view, I reference that class, and I create an object for the class, and add it as a property and synthesize it. I then use
[self.thatClassObject.masterArray addObject:self.viewArray1];

There are two ways you could go about initializing it that I can think of offhand. First, you could alloc/init the array in the app delegate. This will ensure it's created before the rest of the views get a chance to add to it.
EDIT: There's only really one way I can think to do this as Josh Caswell pointed out a good fact that class initializations won't work for this situation. You're best off calling alloc/init for the array either in the app delegate or whichever view is made key window first. You'll have to reference it to the other classes from there

Related

How do I prevent NSMutableArray from losing data?

The first view of my app is a UITableView.
The user will choose an option and the next view will be another UITableView. Now the user can tap on an "add" button to be taken to another UIViewController to enter text in a UITextField. That text will then appear in the previous UITableViewCell when saved.
The issue I am having: if I back out to the main view and then go back to where I previously was, that inputed text is now gone.
How can I make sure that text is not being released or disappears like this?
You might want to store this array somewhere else in your project, like in an MVC (data model). You could create a new class for it that passes the information through the classes and stores the array in one place. Then once you add to the array, you could reference that class and call a method in that class to store the text in the array and whenever you load the table view it loads with that array in the class.
In my case, I would do this, but I would make everything class methods (where you cannot access properties or ivars) and just store the array in the user defaults / web service or wherever you need and retrieve and add/return it like this:
+ (NSMutableArray *)arrayOfSavedData {
return [[NSUserDefaults standardUserDefaults] objectForKey: #"savedData"];
}
+ (void)addStringToArray: (NSString *)stringFromTextField {
[[[[NSUserDefaults standardUserDefaults] objectForKey: #"savedData"] mutableCopy] addObject: stringFromTextField];
}
The mutableCopy part is important because arrays don't stay mutable after you store them into the user defaults
The reason the text is gone, is probably because you're instantiating new controllers when you go back to where you were. You can keep a strong reference to your controllers, and only instantiate one if it doesn't exist yet. Exactly how to do this depends on how you're moving between controllers -- whether you're doing it in code, or using a storyboard for instance.
This kind of issue is very frequent. When you move around multiple controllers and views.
Each time you load a new view and controllers are alloc+init, new set of values are assigned and previous values are not used!!!.
You can use SharedInstance/SingletonClass so that it is allocated & assigned once and does not get re created with new set of values.

Specific Getting index of NSMutableArray Problem

I have a bit of a problem in my code, and i'm beginning to wonder if it's a design issue..
I'm treating my appDelegate class as the primary model for my application. With the appDel class, I have a viewcontroller. In my appdelegate class I store an NSMutableArray called blocks which has all of my block object models. Similarly, in my viewcontroller I have an identical NSMutableArray called blockViews which stores all of my block object views. The way I update the view after receiving notifications from the model is through KVO. In my viewcontroller, I observe each location property of each block in my blocks array (in the app delegate), and update the corresponding blockView in blockViews.
I also have it set up so that whenever I add an object to my blocks array in the appDel, it adds an object to the end of the blockViews' array. Now, my problem arises when I try to remove an object from my blocks array (in the appDel class). I will not always be removing the last object, and so I am stuck as to how to get the actual index of the object I am trying to remove so that I can remove the corresponding index in my blockViews NSMutableArray.
In my observeValueForKeyPath method, the object being passed is the appDelegate class because I am observing the entire blocks array from my viewcontroller (I implemented the Indexed Array Accessors to allow for KVO notifications to occur), and because of this the object being passed into observeValueForKeyPath is in fact a reference to my appDelegateClass (which makes sense).
But, from this, I cannot obtain the index of which object was removed from the blocks array. Does anybody have any suggestions as to how I could alter this to provide an index, or a way to get the correct index?
Is there any way to observe an array for additions or removals, AND have the object being passed into my observeValueForKeyPath method be the block that was added/removed itself, instead of the appdelegate class?
Can't you use indexOfObject... (several variations) to get the index, and then remove it in both arrays? (You realize that NSMutableArray implements all the methods of NSArray, right?)

Add Custom View when Custom Model is Added

My appDelegate class contains an NSMutableArray called blocks, which is an array of custom block objects.
My viewcontroller class has another NSMutableArray called blockViews, which is an array of custom blockView objects. In its init method, it sets it backgroundcolor to a color passed in the parameter, and does some fancy stuff with the border.
Whenever an object is added to my blocks array, I would like to also add a corresponding object to my blockViews array in my viewcontroller, which shares the same location as the location passed to the block object in the blocks array, and adds itself as a subview to the viewcontroller.view.
I would preferably like to use KVO to check whenever an object is added to the blocks array, and then add an object to the blockViews.
I'm relatively new to objective-c programming, but how might someone accomplish this?
Peter Hosey's answer to another question will tell you what you need to know about KVO with NSMutableArray.
In your case, the thing observing the NSMutableArray should be your view controller.

Share NSArray instance to KVO objects

I have a controller class that store a list of contact and I need to share this list to objects that will implement KVO on such array.
The most obvious solution is to use a NSArray instead of NSMutableArray, in order that everyone can use the same instance variable of the controller and it's not possible modify the content of the array.
Now, let's image that this list can change every 3 minutes and only the controller can update the content of the array.
If I dealloc and re-init the contact list array, all my KVO objects will lost the reference to my array.
Finally:
NSArray PRO: No one can alter the content of the array
NSArray CON: The controller can not modify the array without a dealloc, so other objects lost the reference.
NSMutableArray PRO: Controller can modify array content, all the object can easily share the same reference of the array
NSMutableArray CON: Everyone can modify the array.
Any suggestion?
NSArray in my opinion, given that it isn't being updated very often.
The object that owns and modifies the array exposes it through a property of type NSArray*. The observers observe this property. Whenever the object that owns the array recreates it, it sets the property to the new NSArray which then triggers KVO on all of the observers.
Alternatively, you can implement the indexed accessor patterns for the property.

How can I load data for an NSTableView without blocking the interface?

I'm initializing a simple interface, with an NSTableView bound to an array controller (which manages an array of dictionaries). I want to load the content for the array in the background (it's a very time-consuming process), updating the table view every 100 or 1000 elements. The idea is that the interface is available and responsive. I can't figure out how to also trigger an update / refresh afterwards. The table remains empty. Can anyone offer pointers?
My current approach is:
// In init for my app controller. This seems to work well, but I've tried other methods here.
[self performSelectorInBackground:#selector(loadTable) withObject:nil];
- (void)loadTable {
tracks = [[NSMutableArray alloc] initWithCapacity:[masters count]];
// ... create each object one-by-one. Add it to tracks.
for (... in ...) {
[tracks addObject:newObject];
}
// Now I don't know what to do next. The table remains empty.
// Things I've tried (though possibly not in all combinations with the
// method above):
// 1. With a suitably-defined reloadData method, which just reloads
// the table view and sets needs display.
[self performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
// 2. Reload directly.
[tv reloadData];
[tv setNeedsDisplay];
}
If I just load the data directly, and don't try to do that in the background, everything works fine, but it takes almost 30s.
You have the table columns (I assume you meant) bound to an array controller, so that's where the table view gets its data from. The table view may very well be asking for updated arrays, but it's asking the array controller, which doesn't know anything has changed.
The array controller won't simply turn around and ask you for fresh data; that would imply it exists solely to make it harder for you to bind the table view to your array, and that isn't the case. It's a controller; its job is to own (a copy of) the array and maintain its order and the user's selection of some subset of its objects.
Therefore, you need the array controller to find out when you add items to your array. The best way to make this happen is to bind the array controller's contentArray to a property of your controller, and update that property in a KVO-compliant manner.
That means:
Create the mutable array in your init method. (And, of course, release it in dealloc.)
Implement the array accessor methods, plus addTracksObject: and removeTracksObject: (which are technically set accessor methods, so KVO will ignore them for an array property) for your convenience.
To add a track, send yourself an addTracksObject: message. You should respond to that by sending yourself an insertObject:inTracksAtIndex: message (with [self countOfTracks] for the index, unless you want to do an insort), and you should respond to insertObject:inTracksAtIndex: by sending your tracks array an insertObject:atIndex: message.
As I mentioned, KVO will ignore addFooObject: and removeFooObject: when foo is an NSArray property, considering those only NSSet-property accessors, so you need to implement them on top of insertObject:inFooAtIndex: and removeObjectFromFooAtIndex: because those are array accessors, which means KVO will react to them.
Step 3, as I just described it, will be pretty slow, because it will cause the array controller to re-fetch your property and the table view to re-fetch the array controller's arrangedObjects at least once each for every row you add.
So, you should maintain your batch-adding behavior with this alternate step 3:
Implement insertTracks:atIndexes:, and pass it an array of one batch of (e.g., 100 or 1000) tracks and an index set formed by [NSIndexSet indexSetWithRange:(NSRange){ [self countOfTracks], countOfBatch }]. You'll also need to implement removeTracksAtIndexes:, only because KVO will ignore each insert method if you don't also have its counterpart.
You probably should have the array controller set to attempt to preserve the selection, so as not to frustrate the user too much while you're still bringing in rows.
Also, you may want to create the objects on a background thread, periodically sending yourself another batch to add using a main-thread perform. I'm ordinarily an advocate of doing things on the main thread run loop whenever possible, but this sort of thing could easily make your interface laggy while your periodic load builds up another batch.
You need to call setNeedsDisplay:YES on your table view on the main thread. Don't call it from a background thread. All Cocoa UI calls must be done on the main thread or weird things happen.