NSMutableArray KVC/KVO question - objective-c

This is a sample from book "Cocoa Programming For Mac Os X 3rd(HD)" chapter 7 "Key-Value Coding. Key-Vaule Observing
Here is the code:
Person.h:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
NSString *personName;
float expectedRaise;
}
#property (readwrite, copy) NSString *personName;
#property (readwrite) float expectedRaise;
#end
Person.mm:
#import "Person.h"
#implementation Person
#synthesize expectedRaise;
#synthesize personName;
- (id)init
{
[super init];
expectedRaise = 0.05;
personName = #"New Person";
return self;
}
- (void)dealloc
{
[personName release];
[super dealloc];
}
#end
MyDocument.h:
#import <Cocoa/Cocoa.h>
#interface MyDocument : NSDocument
{
NSMutableArray *employees;
}
#property (retain) NSMutableArray *employees;
#end
MyDocument.mm:
#import "MyDocument.h"
#import "Person.h"
#implementation MyDocument
#synthesize employees;
- (id)init
{
if (![super init])
return nil;
employees = [[NSMutableArray alloc] init];
return self;
}
- (void)dealloc
{
[employees release];
[super dealloc];
}
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
#end
And the sample works fine.(A blank table at first and you can add or delete record).
Now I tried to add some records to the array so that the blank table would have
someting in it at first.
Here's what I'v tried(inside the init method):
[self willChangeValueForKey:#"employees"];
Person *p1 = [[Person alloc] init];
[employees addObject: [NSData dataWithBytes: &p1 length: sizeof(p1)]];
[self didChangeValueForKey:#"employees"];
But when I build and wrong I got the error msg:
[<NSConcreteData 0x422bf0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key personName.
......
Can any one help me out of here? Thanks in advance ^_^

That seems like a very reasonable response... you added NSData to your array named employees; guessing from the names, and from the KVC, you probably meant to add p1 to your array instead. So, try:
[employees addObject:p1];

Related

"Missing Context for method declaration" with the constructor

Im getting only this failure after having built the project.
Im using the XCode 4.6.3
class.m
#import "Car.h"
//constructor
-(id)init //<----- ***MISSING CONTEXT FOR METHOD DECLARATION***
{
self = [super init];
if(self){
self.brand = #"";
self.model = #"";
self.vin = 0;
}
return self;
class.h contains no error.
#import <Foundation/Foundation.h>
#interface Car : NSObject
{
NSString *brand, *model;
NSNumber *vin;
}
//set
-(void) setBrand:(NSString *) newBrand;
-(void) setModel:(NSString *) newModel;
-(void) setVIN:(NSNumber *) newVIN;
//get
-(NSString *) getBrand;
-(NSString *) getModel;
-(NSNumber *) getVIN;
//methods
-(void) accelerateTo100;
-(void) fuelConsuming;
-(void) hardStop;
#end
Can you help me with this. Thanks alot.
Answer is what #CodaFi explained. Try this
#import "Car.h"
#implementation Car
-(id)init
{
self = [super init];
if(self){
[self setBrand : #""];
[self setMode1 : #""];
[self setVIN : #""];
}
return self;
}
#end
Implementations of methods related to the Car class are always wrapped in #implementation Car and terminated with an #end. You're declaring and implementing methods without telling the compiler which class they belong to.
Check that you don't have an #import "..." within the #implementation section.

random generator to obtain data from array not displaying

I know theres a better solution using arc4random (it's on my to-try-out-function list), but I wanted to try out using the rand() and stand(time(NULL)) function first. I've created a NSMutableArray and chuck it with 5 data. Testing out how many number it has was fine. But when I tried to use the button function to load the object it return me with object <sampleData: 0x9a2f0e0>
- (IBAction)generateNumber:(id)sender {
srand(time(NULL));
NSInteger number =rand()% ds.count ;
label.text = [NSString stringWithFormat:#"object %#", [ds objectAtIndex:number] ];
NSLog(#"%#",label.text);
}
While I feel the main cause is the method itself, I've paste the rest of the code below just incase i made any error somewhere.
ViewController.h
#import <UIKit/UIKit.h>
#import "sampleData.h"
#import "sampleDataDAO.h"
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *label;
#property (weak, nonatomic) IBOutlet UIButton *onHitMePressed;
- (IBAction)generateNumber:(id)sender;
#property(nonatomic, strong) sampleDataDAO *daoDS;
#property(nonatomic, strong) NSMutableArray *ds;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize label;
#synthesize onHitMePressed;
#synthesize daoDS,ds;
- (void)viewDidLoad
{
[super viewDidLoad];
daoDS = [[sampleDataDAO alloc] init];
self.ds = daoDS.PopulateDataSource;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setLabel:nil];
[self setOnHitMePressed:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)generateNumber:(id)sender {
srand(time(NULL));
NSInteger number =rand()% ds.count ;
label.text = [NSString stringWithFormat:#"object %#", [ds objectAtIndex:number] ];
NSLog(#"%#",label.text);
}
#end
sampleData.h
#import <Foundation/Foundation.h>
#interface sampleData : NSObject
#property (strong,nonatomic) NSString * object;
#end
sampleData.m
#import "sampleData.h"
#implementation sampleData
#synthesize object;
#end
sampleDataDAO.h
#import <Foundation/Foundation.h>
#import "sampleData.h"
#interface sampleDataDAO : NSObject
#property(strong,nonatomic)NSMutableArray*someDataArray;
-(NSMutableArray *)PopulateDataSource;
#end
sampleDataDAO.m
#import "sampleDataDAO.h"
#implementation sampleDataDAO
#synthesize someDataArray;
-(NSMutableArray *)PopulateDataSource
{
someDataArray = [[NSMutableArray alloc]init];
sampleData * myData = [[sampleData alloc]init];
myData.object= #"object 1";
[someDataArray addObject:myData];
myData=nil;
myData = [[sampleData alloc] init];
myData.object= #"object 2";
[someDataArray addObject:myData];
myData=nil;
myData = [[sampleData alloc] init];
myData.object= #"object 3";
[someDataArray addObject:myData];
myData=nil;
myData = [[sampleData alloc] init];
myData.object= #"object 4";
[someDataArray addObject:myData];
myData=nil;
myData = [[sampleData alloc] init];
myData.object= #"object 5";
[someDataArray addObject:myData];
myData=nil;
return someDataArray;
}
#end
what i guess is going on is that nslog function cant print the data inside your sample data class because its not a standard class. not standard classes must implement the "description" method. What you get when you print out your class is the pointer to it, because nslog has no way of knowing how to print out the data in your class.
if what you want to print on that label/nslog is the nsstring inside your class "sampledata" you should access the property.
this can be done in the following way:
SampleData *instanceOfSampleData = (SampleData*)[ds objectAtIndex:number];
label.text = [NSString stringWithFormat:#"object %#", instanceOfSampleData.object];

I'm having problems using a Singleton to pass an array in Objective-c. (code included)

So I'm creating an array called fruits which I would like to share between multiple views. This is my code:
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSMutableArray *fruits;
}
#property (nonatomic, retain) NSMutableArray *fruits;
+ (id)sharedManager;
#end
#import "MyManager.h"
static MyManager *sharedMyManager = nil;
#implementation MyManager
#synthesize fruits;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if ((self = [super init])) {
fruits = [[NSMutableArray alloc] init];
}
return self;
}
-(void) dealloc{
self.fruits = nil;
[super dealloc];
}
#end
Now I'm using the following code so that I can use workouts in the new view
#import "MyManager.h"
#interface Chest : UIViewController {
IBOutlet MyManager *fruits;
}
#property (nonatomic, retain) IBOutlet MyManager *fruits;
-(IBAction) goToList: (id) sender;
#end
When a button is clicked, goToList is called, and I populate my array, fruits
#import "Chest.h"
#implementation Chest
#synthesize fruits;
-(IBAction) goToList:(id)sender{
MyManager *fruits = [MyManager sharedManager];
NSString *filePath;
NSString *fileContents;
filePath = [[NSBundle mainBundle] pathForResource:#"chest_strength" ofType:#"csv"];
fileContents = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
fruits = [fileContents componentsSeparatedByString:#"\n"];
}
When I output the elements of *fruits in this view, everything works fine. Now when I try to access this same variable in the other view, it says the array is null. Here's the code for the second view:
#interface List : UIViewController {
IBOutlet MyManager *fruits;
}
#property (nonatomic, retain) IBOutlet MyManager *fruits;
#end
#import "List.h"
#import "MyManager.h"
#implementation List
#synthesize fruits
NSLog(#"%#", fruits); //This is the part that isn't displaying correctly. I'm getting a null here
So my question is, how do I do this so that I can get the array fruits that I populated in Chest and use it in List so that I can display the contents of the array in that view?
Thank you to everybody who answers. This problem is seriously bogging me down and I need to finish this project ASAP. Much appreciated.
You've changed a few things in this question but the fundamental problem remains: the array is a property of your singleton, and you are not treating it as such.
Whenever you refer to the array, outside of the singleton, do it like this:
[MyManager sharedManager].fruits
If you are accessing it frequently, you can create a local ivar, but you certainly wouldn't have it as an IBOutlet, which you are doing in your last code sample. How is that ever going to be set?

How to Create a Class(es) for storing multi-level data?

I need to store multi-level data for a flash card app I'm writing and I could use some help figuring out 1) How to manage the data and 2) How to store it.
The data is broken down like this:
a) Card contains 2 strings
b) Pack contains a String "PackName" and an array of Cards
c) Deck contains a String "DeckName" and an array of Packs
Right now I have 3 Classes: Card, Pack, Deck.
//Card.h
#interface Card : NSObject {
NSString *primaryPhrase;
NSString *secondaryPhrase;
}
#property (nonatomic,retain)NSString *primaryPhrase;
#property (nonatomic,retain)NSString *secondaryPhrase;
#end
Card.m
#implementation Card
#synthesize primaryPhrase;
#synthesize secondaryPhrase;
-(id)init{
if(self=[super init]){
}
return self;
}
#end
Pack.h
#interface Pack : NSObject{
NSString *packName;
NSMutableArray *cards; //array of card classes
BOOL isInUse;
}
#property (nonatomic,retain)NSMutableArray *cards;
#property (nonatomic,retain)NSString *packName;
#property (nonatomic,assign)BOOL isInUse;
#end
Pack.m
#implementation Pack
#synthesize packName;
#synthesize cards;
#synthesize isInUse;
-(id)init{
if(self=[super init]){
self.isInUse=YES;
}
return self;
}
#end
Deck.h
#interface Deck : NSObject <NSCoding>{
NSString *deckName;
NSMutableArray *packs; //array of pack classes
NSString *primaryLang;
NSString *secondaryLang;
}
#property (nonatomic,retain)NSMutableArray *packs;
#property (nonatomic,retain)NSString *deckName;
#property (nonatomic,retain)NSString *primaryLang;
#property (nonatomic,retain)NSString *secondaryLang;
- (void) encodeWithCoder:(NSCoder*)encoder;
- (id) initWithCoder:(NSCoder*)decoder;
#end
Deck.m
#import "Deck.h"
#implementation Deck
#synthesize packs;
#synthesize deckName;
#synthesize primaryLang;
#synthesize secondaryLang;
//Default settings for each new Deck
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:packs forKey:#"packs"];
[encoder encodeObject:deckName forKey:#"deckName"];
[encoder encodeObject:primaryLang forKey:#"primaryLang"];
[encoder encodeObject:secondaryLang forKey:#"secondaryLang"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
packs=[decoder decodeObjectForKey:#"packs"];
deckName=[decoder decodeObjectForKey:#"deckName"];
primaryLang=[decoder decodeObjectForKey:#"primaryLang"];
secondaryLang=[decoder decodeObjectForKey:#"secondaryLang"];
}
return self;
}
#end
Then I use an NSMutableArray "allDecks" to hold Decks, which in turn contain Cards, but I haven't even been able to get this to work (no errors, but "pack name" is always null):
for(int i=0; i<=2; i++){
Deck *newdeck=[[Deck alloc]init];
[globDat.allDecks addObject:newdeck];
}
((Deck *)[globDat.allDecks objectAtIndex:0]).deckName=#"DeckName 0";
((Deck *)[globDat.allDecks objectAtIndex:1]).deckName=#"DeckName 1";
((Deck *)[globDat.allDecks objectAtIndex:2]).deckName=#"DeckName 2";
for(int i=0; i<=2; i++){
Pack *newpack=[[Pack alloc] init];
[((Deck *)[globDat.allDecks objectAtIndex:i]).packs addObject:newpack];
}
for(int j=0; j<+2; j++){
((Pack *)[((Deck *)[globDat.allDecks objectAtIndex:0]).packs objectAtIndex:j]).packName=#"pack name";
}
NSLog(#"*** NIL sample pack name=%#",((Pack *)[((Deck *)[globDat.allDecks objectAtIndex:0]).packs objectAtIndex:0]).packName);
//always returns null
It's pretty cumbersome to work the structure.
Is this the best way to manage this data?
Plus, the encoding doesn't seem to save the embedded arrays (Pack and Card).
I'll post this as an answer, though it's really an opinion.
I would use Core Data for the model layer. You will not need to deal with serializing your object graph as your are now. Rather, the object graph persistence is largely handled by the framework. There is a learning curve - per Apple, it is not an "entry-level technology" - but it will be much more manageable in the long run.
As for the issue with serialization of the arrays in your object graph, NSMutableArray conforms to the NSCoding protocol; there is some other issue. Instead of:
packs=[decoder decodeObjectForKey:#"packs"];
don't you mean:
self.packs = [decoder decodeObjectForKey:#"packs"];
or
packs = [[decoder decodeObjectForKey:#"packs"] retain];
(I'm assuming you're not using ARC...)
Honestly I would continue with the way you are doing things. The reason that Pack and Card aren't being saved is because they each need to implement the encodeWithCoder: and initWithCoder: methods.
Card.h
#interface Card : NSObject
#property (nonatomic,retain)NSString *primaryPhrase;
#property (nonatomic,retain)NSString *secondaryPhrase;
#end
Card.m
#implementation Card
#synthesize primaryPhrase, secondaryPhrase;
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:primaryPhrase forKey:#"primaryPhrase"];
[encoder encodeObject:secondaryPhrase forKey:#"secondaryPhrase"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
primaryPhrase=[decoder decodeObjectForKey:#"primaryPhrase"];
secondaryPhrase=[decoder decodeObjectForKey:#"secondaryPhrase"];
}
return self;
}
#end
Pack.h
#interface Pack : NSObject
#property (nonatomic,retain)NSMutableArray *cards;
#property (nonatomic,retain)NSString *packName;
#property (nonatomic,assign)BOOL isInUse;
#end
Pack.m
#implementation Pack
#synthesize packName, cards, isInUse;
-(id)init{
if(self=[super init]){
self.isInUse=YES;
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:packName forKey:#"packName"];
[encoder encodeObject:cards forKey:#"cards"];
[encoder encodeObject:[NSNumber numberWithBool:isInUse] forKey:#"isInuse"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
packName=[decoder decodeObjectForKey:#"packName"];
cards=[decoder decodeObjectForKey:#"cards"];
isInUse=[[decoder decodeObjectForKey:#"isInUse"] boolValue];
}
return self;
}
#end
Deck.h
#interface Deck : NSObject <NSCoding>
#property (nonatomic,retain)NSMutableArray *packs;
#property (nonatomic,retain)NSString *deckName;
#property (nonatomic,retain)NSString *primaryLang;
#property (nonatomic,retain)NSString *secondaryLang;
#end
Deck.m
#import "Deck.h"
#implementation Deck
#synthesize packs, deckName, primaryLang, secondaryLang;
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:packs forKey:#"packs"];
[encoder encodeObject:deckName forKey:#"deckName"];
[encoder encodeObject:primaryLang forKey:#"primaryLang"];
[encoder encodeObject:secondaryLang forKey:#"secondaryLang"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
packs=[decoder decodeObjectForKey:#"packs"];
deckName=[decoder decodeObjectForKey:#"deckName"];
primaryLang=[decoder decodeObjectForKey:#"primaryLang"];
secondaryLang=[decoder decodeObjectForKey:#"secondaryLang"];
}
return self;
}

NSKeyedArchiver and description method : app crashes

i tried to test NSKeyedArchiver, but my code seems to be wrong. I then tried only with a NSLog, but the NSLog returns (null) if i alloc-init my var "model" in the init method (in the controller), and it makes the app crash if i put it in viewDidLoad.
Can someone have a look and tell me if something is wrong?
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#class Model;
#interface AAViewController : UIViewController {
Model *model;
}
#property (nonatomic, retain) Model *model;
//+(BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
#end
______
#import "AAViewController.h"
#import "Model.h"
#implementation AAViewController
#synthesize model;
- (id) init {
self = [super init];
if (self) {
model = [[Model alloc] initWithName:#"thomas" age:13];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
//self.model = [[Model alloc] initWithName:#"thomas" age:13];
NSLog (#"%#", self.model);
/*
if (![NSKeyedArchiver archiveRootObject:model toFile:#"test.archive"]){
NSLog (#"erreur");
[model release];
}
*/
NSLog(#"success !");
}
- (void)viewDidUnload {
//model = nil;
}
- (void)dealloc {
[model release];
[super dealloc];
}
#end
______
#import <Foundation/Foundation.h>
//#interface Model : NSObject <NSCoding>
#interface Model : NSObject {
NSString *name;
int age;
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic) int age;
- (id) initWithName:(NSString *)theName age:(int)theAge;
/*
- (id) initWithCoder:(NSCoder *)encoder;
- (void) encodeWithCoder:(NSCoder *)decoder;
*/
#end
______
#import "Model.h"
#implementation Model
#synthesize name, age;
- (id) initWithName:(NSString *)theName age:(int)theAge {
self = [super init];
if (self) {
name = [theName copy];
age = theAge;
}
return self;
}
- (void) dealloc {
[name release];
[super dealloc];
}
/*
- (id) initWithCoder:(NSCoder *)decoder{
self = [super init];
if (self) {
name = [[decoder decodeObjectForKey:#"nom"] retain];
age = [decoder decodeIntForKey:#"age"];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:name forKey:#"nom"];
[encoder encodeInt:age forKey:#"age"];
}
*/
- (NSString *) description {
return [NSString stringWithFormat:#"the name is : %#, %# years old", name, age];
}
#end
Your app is crashing because this line is wrong:
return [NSString stringWithFormat:#"the name is : %#, %# years old", name, age];
age is an int, not an object pointer. Needs to be:
return [NSString stringWithFormat:#"the name is : %#, %d years old", name, age];
I don't think there's anything obviously wrong with your encode/decode code.