Why do objects not get added to my array? - ios

I have two custom classes: FSGame and FSEvent. FSGame has an ivar that should be an NSMutableArray of FSEvent objects.
Here's FSGame.h:
#interface FSGame : NSObject
#property (strong, nonatomic) NSMutableArray *players;
#property (strong, nonatomic) NSString *startTime;
#property (strong, nonatomic) NSString *endTime;
#property (strong, nonatomic) NSMutableArray *gameEvents;
#end
And here's my FSEvent.h:
#interface FSEvent : NSObject
#property NSInteger ID;
#property NSInteger pointTo;
#end
I use
#property (strong, nonatomic) FSGame *game;
to keep an instance of FSGame in my AppDelegate. Then, in my application:didFinishLaunchingWithOptions: I create an instance of FSGame so it can be filled throughout the "game".
_game = [[FSGame alloc] init];
Then, in one of my view controllers, I alloc and init an instance of FSEvent and then attempt to add it to the .gameEvents array:
[appDelegate.game.gameEvents addObject: event];
But something seems to go wrong here because if, from another view controller, I try to get access to that object, it will be nil:
FSEvent *previousEvent = [appDelegate.game.gameEvents lastObject];
if (previousEvent == nil) {
NSLog(#"previousEvent is NIL!");
}
What am I missing here?

It's hard to say with the code that's been shown, but it's possible that you never create the mutable array gameEvents and it's nil when you try to access it or add events to it. Make sure you're creating it somewhere, probably in FSGame's -init:
_gameEvents = [NSMutableArray array];

You need to instantiate the arrays, this does NOT happen when you use
_game = [[FSGame alloc] init];
Try using a lazy instantiation in your FSGame class, add this code
-(NSMutableArray *)gameEvents{
if(!_gameEvents){
_gameEvents = [NSMutableArray array];
}
return _gameEvents;
}
And that should be enough, also make sure of accesing by self.gameEvents and not by _gameEvents!

_game.gameEvents is a pointer to a mutable array. It is not initialized, so its value is nil. There are no any mutable array to add objects to.
You can create it when needed writing this method in FSGame.m:
-(NSMutableArray*) gameEvents {
if ( ! _gameEvents ) {
_gameEvents = [NSMutableArray array];
}
return _gameEvents ;
}

Related

Saving a custom class in an array to be saved

I'm creating an app that will create a form which a user will then fill out and save for later use.
#interface DataModel : NSObject
#property (strong, nonatomic) NSString *whiskeyName;
#property (strong, nonatomic) NSNumber *whiskeyRating;
#property (strong, nonatomic) NSString *whiskeyColor;
#property (strong, nonatomic) NSString *whiskeyNose;
#property (strong, nonatomic) NSString *whiskeyFlavors;
#property (strong, nonatomic) NSString *whiskeyFinish;
#property (strong, nonatomic) NSString *whiskeyNotes;
The app will store multiple copies of these forms (think of Apple's Notes app). I've created a class composed of NSStrings and NSNumbers but I'm having a difficult time figuring out a way save them to an NSArray to access later. I've just started fooling around with Core Data but everything I've found will only save a single form. How can I save multiple versions of a class in an array to be opened and edited for later use? Sorry if the question is vague, but I've been hitting my head on a wall and am having a hard time finding a working solution.
If I understand correctly you have sub-classed NSObject and want to save multiple instances of this class into an array? If that is the case, then a MutableArray should be able to hold any object:
// .m file
#import "DataModel.h"
#interface YourViewController
#property (strong, nonatomic) NSMutableArray *myArray;
#end
#implementation YourViewController
- (void) viewDidLoad {
NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:numberOfObjectsToStore];
DataModel *myClassInstance1 = [[DataModel alloc] init];
myClassInstance1.whiskeyName= #"somevalue";
myClassInstance1.whiskeyRating= 5;
DataModel *myClassInstance2 = [[DataModel alloc] init];
myClassInstance2.whiskeyName= #"someothervalue";
myClassInstance2.whiskeyRating= 2;
[myArray addObject:myClassInstance1];
[myArray addObject:myClassInstance2];
}

Accessing properties from Object within NSMutableArray not working

I have the class LearnfestItem.h :
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface LearnfestItem : NSObject
#property (strong, nonatomic) NSString * itemId;
#property (strong, nonatomic) NSString * itemTitle;
#property (strong, nonatomic) NSString * itemDescription;
#property (strong, nonatomic) NSString * itemContent;
#property (strong, nonatomic) NSString * itemType;
#property (strong, nonatomic) UIImage * itemImage;
#property (strong, nonatomic) NSDate * itemRegistered;
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
& Object.m :
#import "LearnfestItem.h"
#import "Defaults.h"
#implementation LearnfestItem
-(id)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
self = [super init];
self.itemId = data[ITEM_ID];
self.itemTitle = data[ITEM_TITLE];
self.itemDescription = data[ITEM_DESCRIPTION];
self.itemContent = data[ITEM_CONTENT];
self.itemType = data[ITEM_TYPE];
self.itemImage = image;
self.itemRegistered = data[ITEM_REGISTERED];
return self;
}
#end
In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:
LearnfestItem * createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %li", createLearnfestItem.itemId, (long)row);
[self.learnfestItemObjects insertObject:createLearnfestItem atIndex:row];
On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:
self.selectedLearnfestItem = [self.learnfestItemObjects objectAtIndex:indexPath.row];
Then I want to send it to another view controller to present the data I do this in the prepareForSegue segement:
LearnfestItemViewController * learnfestVC = [segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;
When I try to access my LearnfestItem's properties within cellForRowAtIndexPath. All I get is null... and so forth in my other table view delegate methods.. Can anyone spot what i'm doing wrong? Thanks
Code you show is missing all error and validity checking. When using NSAssert() and item.length/item.count checks, you will know what's going on.
However, based on your code I'd suggest two things:
All objects which also have mutable version should use "copy" properties. Now you have "strong" pointer to data received via init method, but if you reset the originally given data variable to point elsewhere (e.g. reusing one data variable inside for loop to init multiple items), then... I don't know what your LearnfestItem item properties will point to.
Make sure your LearnfestItemViewController *item is a copy, too.
My guess is that everything is initialised correctly, but the data disappears afterwards.
For example:
#property (nonatomic, copy) NSString * itemId; // Safe
#property (nonatomic, strong) NSString * itemId; // Not safe
When your property class has mutable variations (e.g. NSString vs. NSMutableString vs. MyMutableString), copy is safer. Using strong will create a pointer to original data, which could have been a mutable instance and could be modified afterwards. Using strong will always point to original data, even after it has been changed.
Second part:
learnfestVC.item = self.selectedLearnfestItem;
Your LearnfestItemViewController contains some property related to LearnfestItem class. Make sure it's a copy, too. When using segues, the calling object quite often just disappears. Make sure your new controller has local copy of all needed data (or use a protocol delegate, but that's a longer discussion)
Adding error and data validity checking will make your task a lot easier. Instead of trying to figure out afterwards why something doesn't work, you'll get notifications when something isn't as you're expecting.
Here's quick and dirty "maintenance" for your code. What you should get out of this is some ideas what to check, where and how. In normal situations this is overkill, but now you have a mysterious problem and need to find it. It can be hard and monotonous work.
#import Foundation;
#import UIKit;
#interface LearnfestItem : NSObject
#property (copy, nonatomic) NSString *itemId;
#property (copy, nonatomic) NSString *itemTitle;
#property (copy, nonatomic) NSString *itemDescription;
#property (copy, nonatomic) NSString *itemContent;
#property (copy, nonatomic) NSString *itemType;
#property (strong, nonatomic) UIImage *itemImage;
#property (strong, nonatomic) NSDate *itemRegistered;
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
Object.m :
#import "Defaults.h"
#import "LearnfestItem.h"
#implementation LearnfestItem
- (instancetype)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
NSAssert(data.length, #"My Assert: missing data");
NSAssert(image, #"My Assert: missing image");
if ((self = [super init]))
{
// TODO: nil ok, if doesn't exist?
_itemId = data[ITEM_ID];
_itemTitle = data[ITEM_TITLE];
_itemDescription = data[ITEM_DESCRIPTION];
_itemContent = data[ITEM_CONTENT];
_itemType = data[ITEM_TYPE];
_itemImage = image;
_itemRegistered = data[ITEM_REGISTERED];
}
return self;
}
#end
"(original text) In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:"
NSAssert(learnfestItemDictionary.count, #"My Assert: missing dict");
NSAssert(learnfestItemImage, #"My Assert: missing image");
LearnfestItem *createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %#", createLearnfestItem.itemId, #(row));
NSAssert(createLearnfestItem, #"My Assert: missing item");
NSAssert(self.learnfestItemObjects.count > row, #"My Assert: learnfestItemObjects");
self.learnfestItemObjects[row] = createLearnfestItem;
"(original text) On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:"
NSAssert(self.learnfestItemObjects.count > indexPath.row, #"My Assert: learnfestItemObjects");
self.selectedLearnfestItem = self.learnfestItemObjects[indexPath.row];
"(original text) Then I want to send it to another view controller to present the data I do this in the prepareForSegue segment:"
LearnfestItemViewController *learnfestVC = (LearnfestItemViewController *)[segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;

Create an array that hold custom class object

I have class, that have several properties, it look like this:
#interface PlaceHolder : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *webPage;
#property (strong, nonatomic) NSNumber *latitude;
What i need is, to create an array, that hold objects of that class. Obvious, properties will not be nil and will be different each time. So, that array must have several hundreds of PlaceHolder object, and it should be possible to get information for any of that object and it properties. But, when i try to create that array, in NSLog i see that it contain only (NULL) objects. This is how i try too add object to array:
In header i wrote:
#property (strong, nonatomic) PlaceHolder *place;
Then:
self.place = [[PlaceHolder alloc]init];
self.place.name = nameString;
NSLog(#"%# name???", self.place.name);
[self.placeObjectsArray addObject:self.place];
self.place.name is not nil, and still, array is empty. Well, its not true, it not empty but, it only contains (null) objects. How to fill array with objects of my class?
Any advice would be appreciated, thanks!
UPDATED:
I init array like this -
-(id)initWithDelegate:(id)delegateObj{
...
self.placeObjectsArray = [NSMutableArray array];
...
return self;
}
UPDATED: Now, when i try to init-alloc array in same method (instead of setting #property and strong relation) i can see it in NSLog. I wonder why it won't happen when i use my array, that set as property..
You need to alloc-init your Mutable Array ;
NSMutableArray *myArray = [[NSMutableArray alloc]initWithObjects:self.place,nil];
or simply
NSMutableArray *myArray = [[NSMutableArray alloc]init];
Then you could add objects with a for loop or whatever you need, using the following :
for ( YOURINSTRUCTION )
{
[myArray addObject:YOUROBJECT]
}
I recommend to lazy instantiate the array, that way it will only get instantiated when really needed. Since you are setting the array as a property, you can override the getter method for it like this:
- (NSMutableArray *)placeObjectsArray
{
if (!_placeObjectsArray) _placeObjectsArray = [[NSMutableArray alloc] init];
return _placeObjectsArray;
}
With this, you can call [self.placeObjectsArray addObject:self.place] anywhere in your code and the array will always be initialized when needed.

Not able to access NSMutableArray from another class

I've read through many posts here but I can't seem to make my array available in another class.
I want to access the array plusTransactions in CPPHistoryViewController (a table controller, in a container as a child of CPPSecondViewController) from class CPPSecondViewController.
CPPSecondViewController.h
#import <UIKit/UIKit.h>
#interface CPPSecondViewController : UIViewController <UIWebViewDelegate>
#property (weak, nonatomic) IBOutlet UIWebView *webView;
#property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedController;
#property (weak, nonatomic) NSMutableArray *plustransactions;
#property (weak, nonatomic) NSMutableArray *campustransactions;
#property (weak, nonatomic) NSMutableArray *mealtransactions;
#end
CPPSecondViewController.m
#import "CPPSecondViewController.h"
#interface CPPSecondViewController ()
#implementation CPPSecondViewController
#end
#synthesize plustransactions = _plustransactions;
#synthesize campustransactions = _campustransactions;
#synthesize mealtransactions = _mealtransactions;
...
_plustransactions = newPlustransactions;
NSLog(#"%#", newPlustransactions);
NSLog(#"%#%lu", #"That's how many: ", (unsigned long)_plustransactions.count);
CPPHistoryViewController *historyView = [[CPPHistoryViewController alloc]init];
[historyView.tableView reloadData];
This is good, returns all of my array items and count is 7.
CPPHistoryViewController.h
#import <UIKit/UIKit.h>
#interface CPPHistoryViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
#end
CPPHistoryViewController.m
#import "CPPHistoryViewController.h"
#import "CPPSecondViewController.h"
#import "CPPPlusTransaction.h"
#import "CPPCampusTransaction.h"
#import "CPPMealTransaction.h"
#import "CPPHistoryCell.h"
#interface CPPHistoryViewController ()
#end
#implementation CPPHistoryViewController
...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
CPPSecondViewController *secondView = [[CPPSecondViewController alloc] init];
NSMutableArray *plusTransactions = [[NSMutableArray alloc]init];
plusTransactions = secondView.plustransactions;
NSMutableArray *campusTransactions = [[NSMutableArray alloc]init];
campusTransactions = secondView.campustransactions;
NSMutableArray *mealTransactions = [[NSMutableArray alloc]init];
mealTransactions = secondView.mealtransactions;
NSLog(#"Table formatting called");
NSLog(#"%ld", (long)secondView.segmentedController.selectedSegmentIndex);
switch (secondView.segmentedController.selectedSegmentIndex) {
case 0:
NSLog(#"%#%lu", #"PlusTransactions", (unsigned long)plusTransactions.count);
return plusTransactions.count;
break;
case 1:
NSLog(#"%#%lu", #"CampusTransactions", (unsigned long)campusTransactions.count);
return campusTransactions.count;
break;
case 2:
NSLog(#"%#%lu", #"MealTransactions", (unsigned long)mealTransactions.count);
return mealTransactions.count;
break;
}
return 1;
}
...
#end
Here's where things get weird. Any array I call from here returns with a count of 0. Any ideas?
I assume what you want is to get the array data stored in CPPSecondViewController to be availalbe in CPPHistoryViewController. But as per the code you posted,
CPPHistoryViewController *historyView = [[CPPHistoryViewController alloc]init];
[historyView.tableView reloadData];
This just create new instance and the tableView inside the view is reloaded. But in cellForRowAtIndexPath, you are creating new instance of CPPSecondViewController, thats wrong.
What you can do is, simply pass the CPPSecondViewController instance before reloading tableView.
In CPPHistoryViewController.h, create a property for keeping secondView like this
#property (strong, nonatomic) CPPSecondViewController *secondView;
then, change your code in CPPSecondViewController like below:
CPPHistoryViewController *historyView = [[CPPHistoryViewController alloc]init];
historyView.secondView = self;
[historyView.tableView reloadData];
Also, remove alloc init statements from cellForRowAtIndexPath, just use secondView.plustransactions here.
Hope this helps!
CPPSecondViewController *secondView = [[CPPSecondViewController alloc] init];
Here you are creating a new object, So all values are nill.
Here you can only set values in to second view controller.
in your History class, you nedd to create a property of a second class they you can set value in to Second class
#property (nonatomic, retain) Secondclass* secondclass;
[secondClass setAry: ary];
CPPSecondViewController *secondView = [[CPPSecondViewController alloc] init];
in this code, the view controller is just init, the properties is all nil, it's a new view controller instance.
setup properties in init method, or access the second view's view property, to load view, to trigger the property initialize.
if you want reference the second view controller from history view controller, you can add a property in history view controller refer to second view controller.
in CPPHistoryViewController.h
#property (weak, nonatomic) CPPSecondViewController *secondViewController;
and set this property in your show code.
in CPPSecondViewController.m
_plustransactions = newPlustransactions;
NSLog(#"%#", newPlustransactions);
NSLog(#"%#%lu", #"That's how many: ", (unsigned long)_plustransactions.count);
CPPHistoryViewController *historyView = [[CPPHistoryViewController alloc]init];
historyView.secondViewController = self;
[historyView.tableView reloadData];

Deep copy of an NSMutableArray of custom objects with NSMutableArray members

I'm trying to make a deep copy of an NSMutableArray whose objects are instances of a custom class similar to this:
#interface CustomParent : NSObject
#property NSInteger Id;
#property (strong, nonatomic) NSString *IdStr;
#property (weak, nonatomic) NSDate *Date;
#property (strong, nonatomic) NSMutableArray *CustomChildren;
#property (strong, nonatomic) CustomType *Type;
#property float Value;
#end
I know there are lots of posts dealing with copying objects, but I donĀ“t find examples for getting a complete copy of objects with collection members or properties. NSMutableArray *dstArray = [[NSMutableArray alloc] initWithArray:srcArray copyItems:YES]; raises an exception involving the copyWithZone method.
How can I do this? Thanks!
In order to deep copy the content of the array
[[NSMutableArray alloc] initWithArray:srcArray copyItems:YES];
will send copyWithZone: to every object inside the collection. If they don't respond to this selector, you'll get a crash.
Have your CustomParent class to conform to the NSCopying protocol and you're done.
Here's some extra info on how do achieve it: Implementing NSCopying

Resources