NSMutableArray changing old stored data issue - ios

I am parsing JSON and storing that data into NSMutableArray on my homepage. I want to access that data on my other page not after the another, so I am using Singleton Pattern for sharing common data in my homepage. I am storing that NSMutableArray data to Singleton file for common access. In second page i am showing that data in tableview. There is one button which modifies the data but will not change original data i.e homepage arraydata. So I am storing that data into clonearray and updating that clonearray. But when I return back to homepage and in ViewDidAppear after placing breakpoint I am observing my original data is changed.
Please have a look on this issue and correct me where I am doing wrong with my code.
For more reference I added the project link please download and check this: [Project Link][1]
In this project I have created the 2 View Controllers , 1 model class, and one Singleton for common data across all file.
View Controller:
In this file I am parsing JSON and storing in NSMutableArray and also setting the same array data to Singleton file array for common access across all file.
SecondViewController:
In this file I am getting array data from Singleton file and storing in local array.
There is one button to modify data. In modify data function I am modifying the data and storing in the other array for common access (clonearray).
But when I return back to First View Controller and in ViewDidAppear after placing breakpoint, I am observing my original data is changed.
Please check, I don't want my original data to be changed. Please check and suggest me where I am doing wrong with my code.

Your problem because all your property( ViewController->shippingArray, SecondPageController->myshippingarray, PersonDtklSingleton->shippingAddress) point to same array.
You can fix problem by clone array with Deep Copies, to copy original to new array. Make sure all items in original array adopted NSCopying.
SecondPageController
#implementation SecondPageController
...
- (void)viewDidLoad {
[super viewDidLoad];
myshippingarray=[[NSMutableArray alloc]init];
// deep copy
self.myshippingarray = [[NSMutableArray alloc] initWithArray:[[PersonDtklSingleton sharedInstance]shippingAddress] copyItems:YES];
// Do any additional setup after loading the view.
}
...
#end
PersonModel
#implementation PersonModel
#synthesize housernumber,resident,street,city,pincode,defaultAddress;
- (id)copyWithZone:(NSZone *)zone {
PersonModel *copyObj = [PersonModel new];
copyObj.housernumber = [self.housernumber copyWithZone:zone];
copyObj.resident = [self.resident copyWithZone:zone];
copyObj.street = [self.street copyWithZone:zone];
copyObj.city = [self.city copyWithZone:zone];
copyObj.pincode = [self.pincode copyWithZone:zone];
copyObj.defaultAddress = [self.defaultAddress copyWithZone:zone];
return copyObj;
}
#end

If I did't wrong when you update the data it change both of your data since it's referencing the same memory
you need to alloc a new memory and copy the data into a new array
like this
NSMutableArray *newArray=[[NSMutableArray alloc] initWithArray:originalArray copyItems:YES];
You just update the newArray and the original one won't be affected

Related

iOS: How to share data between different views of Tab Bar Controller

My app has two views managed by a Tab Bar Controller. One of the views is Google Map (GMSMapView using their SDK) and the other is a TableView showing a list of the same data. The markers on the map are the same data in the TableView (just alternate presentations of the same data).
I fetch the data from an NSURLSessionDataTask. I'm wondering what is the best way to share that data between the two views. Obviously, I don't want to fetch the data twice for each view. But I'm not sure what is the best practice for making that shared data available/synched between the two views.
A similar question was asked but not answered here.
You can create a model class which holds the map related data in an array/dictionary/custom class objects. You can make this model class as a singleton(can be initialized only once). Both view controllers (i.e the map and table view) can refer to this model to populate date in different views now.
Model Class
-----------
#property (strong, nonatomic) MyCustomDataRepresentationObj *data;
+ (id)sharedModel {
static MyModelClass *sharedModel = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedModel = [[self alloc] init];
});
return sharedModel;
}
-(void)fetchMapDataWithCompletionBlock:(void(^)(id response, NSError *error)onComplete
{
// Check if data is available.
// Note: You can add a refresh data method which will fetch data from remote servers again.
if (!data) {
__weak MyModelClass *weakSelf = self;
// Make HTTP calls here, assume obj is returned value.
// Convert network response to your data structure
MyCustomDataRepresentationObj *objData = [MyCustomDataRepresentationObj alloc] initWith:obj];
// Now hold on to that obj in a property
weakSelf.data = objData;
// Return back the data
onComplete(objData, error);
} else {
onComplete(objData, nil); // Return pre fetched data;
}
}
Now in view controllers you would have to call the model class method which will inturn make the network call(if needed) and returns data in completion block.
View Controller 1
-----------------
-(void)viewDidLoad
{
// This is where the trick is, it returns the same object everytime.
// Hence your data is temporarily saved while your app is running.
// Another trick is that this can be accessed from other places too !
// Like in next view controller.
MyModel *myModelObj = [MyModel sharedModel];
// You can call where ever data is needed.
[myModelObj fetchMapDataWithCompletionBlock:^(id response, NSError *error){
if (!error) {
// No Error ! do whats needed to populate view
}
}];
}
Do the same in other view controller.
View Controller 2
-----------------
-(void)viewDidLoad
{
// Gets the same instance which was used by previous view controller.
// Hence gets the same data.
MyModel *myModelObj = [MyModel sharedModel];
// Call where ever data is needed.
[myModelObj fetchMapDataWithCompletionBlock:^(id response, NSError *error){
if (!error) {
// No Error ! do whats needed to populate view
}
}];
}
Note: I have just jotted down these lines of code here, there might be syntax errors. Its just to get the basic idea.
A UITabBarController act as a Container.
So from your 2 child ViewControllers, you can access the TabBarViewController with the property parentViewController.
So if you want to share the same data with your 2 child ViewControllers, you can fetch and store your data in your UITabBarController. And, from your UIViewControllers, you can access it like this
MyCustomTabBarController *tabBar = (MyCustomTabBarController*)self.parentViewController;
id data = tabBar.myCustomData;
Use Singleton Patterns create a singleton class and initialize singleton instance in your AppDelegate.m this way you can access your singleton class instance from your AppDelegate by using
How about a data fetching object? Make a new class that makes requests for your data bits and stores the results internally.
You then could get the data into your ViewController with a number of different methods:
Direct Reference Associate this object with each ViewController as a property on the ViewControllers before setting the viewControllers property on the Tab Bar Controller.
Your interface to this new class could include the set of fetched results, as well as a method (with a callback when the request finished perhaps) to tell the object to fetch more results.
Notification Center Your object could post notifications when it has more data, and just include a method to start requesting more data.
Delegate + Registration You could create a protocol for objects that want to get told about changes to the data set, make sure all of your necessary ViewControllers conform, and have a delegates NSArray property on your data fetching object. This is far more manual than Notification Center, but it's slightly easier if you need a very robust interface.
Needless to say, there are a lot of ways to handle this, but they all start with designating a class to do the specific task of fetching/storing your data.

How to use one array in different classes?

I have an array. It is filled in one class named "SampleDataDAO".
What i need:
In the 2nd class named "MainMenu" i need to keep this code:
- (void)viewDidLoad
{
[super viewDidLoad];
daoDS = [[SampleDataDAO alloc] init];
self.ds = daoDS.PopulateDataSource;
}
And in the third class named "HView" i need to use array "ds" (NSMutableArray).
But i need to use it already filled from 2nd class, to return the count of elements:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return ds.count;
}
Thanks for helping!
Why should not you have a singleton class, and have that array as a member of that singleton class.
More info on singleton found here
Rather than using singletons, or getting a reference to the array from your appDelegate (which is just the same as using a singleton). A better way would be to pass the array as a reference to the view controller when you create it.
This is similar to the way that you pass a managed object context to view controllers if you are using Core Data, rather than calling down to the app delegate to get it.
Why don't u try to use constant file for this type or requirement.
Use these files for your requirement.
Constant.h
Constant.m
Import Constant.h file in your viewController and use that methods to set and get array directly with class name as
NSArray *array=[Constant getArray];
Or
[Constant setArray:array];

Accessing properties of an object inside array of objects

I have a list of items showing up on a table view.
Every item has its properties such as name, pic, rank etc'.
My goal is, every time the user selects a row the item with its properties will be added to a new list.
I've created a new list called listOfBugs and because i want it to be global i've allocated and initialized it inside viewDidLoad. (Is that a proper thing to do?)
Here is my code:
MasterViewController.h
#interface MasterViewController : UITableViewController
{
NSMutableArray *listOfBugs;
}
#property (strong) NSMutableArray *bugs;
MasterViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
listOfBugs = [[NSMutableArray alloc]init];
self.title = #"Scary Bugs";
}
...
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:bug.data.title delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[messageAlert show];
[listOfBugs addObject:bug];
NSLog(#"this is %#",listOfBugs);
}
Using NSLog I can see that the objects are added:
ScaryBugs[1195:11303] this is <ScaryBugDoc: 0x75546e0>
2012-12-05 17:45:13.100
ScaryBugs[1195:11303] this is <ScaryBugDoc: 0x75546e0>
I have a few questions.
1.How can I access the properties of the objects inside of the array listOfBugs ?
Update: This worked for me:
NSLog(#"this is %#",((ScaryBugDoc *)[listOfBugs objectAtIndex:0]).data.title);
But I can't access the listOfBugs from another class.
I turned it into a property as suggested to make my life easier but still can't access it from another class.
For example in listOfBugsViewController.m return [_listOfBugs count]; will give me the error Use of undeclared identifier '_listOfBugs'
2.I want to be abale to populate a table view with the customized list, how can i do that?
After accomplishing that I would like to save the list as a plist and also add and remove objects from it at ease so I need to take that under consideration.
This is the code that I'm based on, I only made a few adjustments to create the new list
This is really two questions:
1) How do I make my property a public property which can be accessed by other classes?
You do this just like you did with your bugs property. Add this to your .h file:
#property (strong) NSMutableArray *newList;
Note that if you aren't using different threads, you can make it a little more efficient by using the nonatomic property as well (#property (nonatomic, strong)).
Once you do that, you don't need your iVar declaration because it will automatically be generated for you. (i.e. you can remove NSMutableArray *newList;.)
2) How do I access an object in an array?
Objects in an array are stored as an id object, meaning that it is a "generic" object. If you know what type of object is stored, then you need to tell the compiler what it is so that it knows what properties and methods are appropriate for that class. You do this by casting the variable to the proper type:
ScaryBugDoc *bug = (ScaryBugDoc *)[self.newList objectAtIndex:0];
Then, you can access the properties of the object, assuming that they are public (as covered in point 1 above) like this:
NSLog(#"this is %s", bug.data.tile);
Okay, so based from the comments, this should work:
Album* tempAlbum = [albumList objectAtIndex:i];
//now you can access album's properties
Song* tempSong = [album.songs objectAtIndex:j];
//now you can access song's properties
This can be simplified down to:
Song* someSong = [((Album)[albumList objectAtIndex:i]).songs objectAtIndex:j];
When returning an object from an NSArray, or a collection object like that it will return a generic id object. This will need to be typecasted to the expected object so you can access the right properties.

Proper way of creating new objects which are copies of NSDictionary and NSArray objects defined in app delegate

I am wondering what the correct way is to make a copy of an object defined in the app delegate or a singleton object. In short, I am making an app which requires a user to login. This login view is just a modal view controller on top of the 'real' app, which consists of a tabbarcontroller, plus some tableview controllers. After a successful login, there is send a data request to a remote server, and the modal view controller is dismissed, revealing the tabbar controller and table views holding the XML data. To parse the incoming data, I have created a singleton object named DataParser, which has interface
...
#interface DataParser : NSObject {
// Data objects that hold the data obtained from XML files
NSMutableDictionary *personnel;
NSMutableDictionary *schedule;
NSMutableDictionary *today;
}
#property (nonatomic, retain) NSMutableDictionary *personnel;
#property (nonatomic, retain) NSMutableDictionary *schedule;
#property (nonatomic, retain) NSMutableDictionary *today;
...
Now in these dictionaries I store (mutable) dictionaries and arrays holding NSString objects with the parsed XML data. Since I do not want to modify these original objects holding the parsed data (that is to say, I only want to modify them at the login stage, but not in any of the tableview controllers), I am creating a new dictionary object which holds a copy of the content of one of the dictionaries above in each tableview controller. So for instance, in the loadView of a view controller called ScheduleViewController I have
...
#interface ScheduleViewController : UITableViewController {
NSDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [NSDictionary dictionaryWithDictionary:sharedSingleton.schedule];
}
...
Now this seems to work fine. The only difficulty arises however, when the user 'logs out', which entails popping the login modal view controller back on the stack. When the user presses the login button again, then a new XML data request is send to the server and the dictionaries in the singleton object get refreshed with the (new) data (I check if they contain any data, if so I call removeAllObjects before filling them up again with newly parsed data). At this point the dictionaries in all view controllers should be updated too, however I am not quite sure how to go about this the right way. I have noticed that loadView is not always called again in this case and so to this end I have added the same code as above in loadView to every viewWillAppear method. After navigating back and forth between the different views or navigating back and forth between child views of a tableview a couple of times, I receive an EXC_BAD_ACCESS error however. I suspect this has to do with not properly retaining the copies of the original dictionaries, but I don't seem to be able to find a solution around this. Instead of using dictionaryWithDictionary, which I suspect is not the right way to go anyway, I also tried a different approach, where instead of using objects of type NSDictionary in ScheduleViewController I use NSMutableDictionary. So:
...
#interface ScheduleViewController : UITableViewController {
NSMutableDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSMutableDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [[NSMutableDictionary alloc] initWithDictionary:sharedSingleton.schedule];
}
- (void)viewWillAppear {
DataParser *sharedSingleton = [DataParser sharedInstance];
[self.copyOfSchedule removeAllObjects];
[self.copyOfSchedule addEntriesFromDictionary:sharedSingleton.schedule];
[self.tableView reloadData];
}
...
But this doesn't get rid of the EXC_BAD_ACCESS errors. To make a very long story short: what would be the best way to go about making independent copies of objects defined in a singleton object or app delegate and which can be dynamically updated at request? Since I am already rather into the project and lots is going on, I realize that my question may be a bit vague. Nonetheless I hope there is somebody who could enlighten me somehow.
Deep copies are often made recursively. One way to do it would be to add -deepCopy methods to NSDictionary and NSArray. The dictionary version might go like this:
- (NSDictionary*)deepCopy
{
NSMutableDictionary *temp = [self mutableCopy];
for (id key in temp) {
id item = [temp objectForKey:key];
if ([item respondsToSelector:#sel(deepCopy)] {
// handle deep-copyable items, i.e. dictionaries and arrays
[temp setObject:[item deepCopy] forKey:key]
}
else if ([item respondsToSelector:#(copy)]) {
// most data objects implement NSCopyable, so will be handled here
[temp setObject:[item copy] forKey:key];
}
else {
// handle un-copyable items here, maybe throw an exception
}
}
NSDictionary *newDict = [[temp copy] autorelease];
[temp release]
return newDict;
}
I haven't tested that, so be a little careful. You'll want to do something similar for NSArray.
Note that views are not copyable.
It is quite a typical pattern that you build an array or dictionary with some code, so clearly it must be mutable while you add bits to it, and when you're done you don't want it ever to change. To do this:
Have a property like
#property (...) NSArray* myArray;
When you calculate the contents of myArray, use a mutable array to build it, like
NSMutableArray* myMutableArray = [NSMutableArray array];
When you're done building the array, just use
self.myArray = [NSArray arrayWithArry:myMutableArray];

Obj - C: Having trouble creating a UITableView that updates cells from an HTTP API in real-time (one at a time)

I am polling an HTTP API - it returns one item at a time, in real-time (about every 4 seconds). As each item is received, I would like a new UITableView cell to be populated. The full list of received items must remain in a class property, I'm guessing an NSMutableArray. What is the best way to initialize an NSMutableArray as a class property, update it as new information comes in, and then use the count to update a new UITableViewCell?
Here's how I'm adding content to an NSMutableDictionary:
NSMutableDictionary *messageContents = [[NSMutableDictionary alloc] init];
[messageContents retain];
[messageContents setValue:messageText forKey:#"text"];
[messageContents setValue:image forKey:#"image"];
[self addMessageToDataArray:messageContents];
Here's the method stuffing objects into the array:
- (void)addMessageToDataArray:(NSArray *)messageDictionary {
[self.messageDataArray addObject:messageDictionary];
NSLog(#"count = %#", [self.messageDataArray count]);
[self reloadTableData];
}
At this point, calling count on the messageDataArray class property crashes the application. I'm very used to working with arrays in Actionscript, Obj-C is obviously totally different. Please explain the method for instantiating an NSMutableArray as a class property, filling it with NSMutableDictionary's and then finding the NSMutableArray count (which will be dynamically updating in real-time) so I can use that info to update a UITableView (on the fly).
Or... tell me I'm being silly and suggest a much easier solution.
From your description I would guess you're not allocating the messageDataArray before using it.
The init function for your table view (controller?) class should have a line like this
messageDataArray = [[NSMutableArray alloc] initWithCapacity:20];
It's also worth checking that you have [messageDataArray release]; in your dealloc method.

Resources