accessing inherited array in Objective C - objective-c

I'm currently working on a project that's split in two classes, the class 'Array' and the class 'PPCalcVals'. Because other classes, that will be added, will also have to have access the array, I thought it would be the best to write the array class, containing an NSMutableArray and Subclass all the others (beginning with the PPCalcVals class.
So the 'PPCalcVals' class needs to access the elements of the array in the superclass 'Array'.
(Please correct me if this is the wrong approach).
As mentioned the whole program is written and well working in C but to create a GUI and eventually an OSX or IOS application I started to learn OOProgramming with Objecitve C.
Anyways, when I reference the objects in the array of the superclass the only value that gets printed is "null" which is not really what I want.
Here is the code:
main routine:
#import <Foundation/Foundation.h>
#import "Array.h"
#import "PPCalcVals.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
Array *prices = [[Array alloc]initWithName:#0];
PPCalcVals *myVals = [[PPCalcVals alloc]init];
[prices addValue:#12];
[prices addValue:#13];
[prices addValue:#14];
[prices addValue:#15];
[prices addValue:#15];
[prices print];
[myVals print];
}
return 0;
}
array.h file:
#import <Foundation/Foundation.h>
#interface Array : NSObject
{
NSMutableArray *prices;
}
-(id) initWithName: (NSNumber *) values;
-(void) addValue: (NSNumber *) value;
-(void) print;
-(NSMutableArray *) prices;
#end
array.m
#import "Array.h"
#implementation Array
-(id) initWithName:(NSNumber *)values
{
self = [super init];
if(self)
{
prices = [NSMutableArray array];
}
return self;
}
-(void) addValue: (NSNumber *) value
{
[prices addObject:value];
}
-(void) print
{
NSLog(#"%#",prices);
}
-(NSMutableArray *)prices
{
return prices;
}
#end
PPCalcVals.h:
#import "Array.h"
#interface PPCalcVals : Array
#property id high,low,open,close;
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)l; //set high and low
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c; //set open and close
-(void) sort; //sort array
-(void) print; //debugging tool
#end
PPCalcVals.m:
#import "PPCalcVals.h"
#implementation PPCalcVals
#synthesize high,low,open,close;
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c
{
o = prices[0];
c = prices[2];
open = o;
close = c;
}
-(void) sort;
{
[prices sortedArrayUsingComparator:^(NSString *str1, NSString *str2) {
return [str1 compare:str2 options:NSNumericSearch];
}];
}
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)
{
h = prices[0];
l = prices[2];
high = h;
low = l;
}
-(void) print
{
NSLog(#"open: %#",open);
NSLog(#"close: %#",close);
NSLog(#"high: %#",high);
NSLog(#"low: %#",low);
}
#end
The running program outputs only:
2013-08-05 10:21:08.546 prog1[1314:303] (
12,
13,
14,
15,
15
)
open: (null)
close: (null)
high: (null)
low: (null)
I realize that this is probably a really basic question but I would appreciate your help a lot and I already thank you if you read until this point ;)

You have created two distinct, completely unrelated objects prices and myVals:
Array *prices = [[Array alloc]initWithName:#0];
PPCalcVals *myVals = [[PPCalcVals alloc]init];
What you probably meant is to create a PPCalcVals object (which inherits
all methods from the superclass Array):
PPCalcVals *prices = [[PPCalcVals alloc]init];
[prices addValue:#12]; // invokes `addValue` method from superclass "Array"
// ...
[prices print]; // invokes `print` method from "PPCalcVals"
You also have to implement init in the PPCalcVals class in such a way
that it calls the "designated initializer" initWithName in the superclass.

I have one question. Where did you make calls for the functions
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)l;
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c;
Because all I can see is that these functions are not called. So the properties high, low, open & close are not yet assigned and thus they return Null.
Also may I know the reason why are you overriding the parameters inside above mentioned functions?
o = prices[0];
c = prices[2];
and
h = prices[0];
l = prices[2];

Related

Working with Class, properties and initialization

I'm working on this assignment I found online (Intermediate App Development Using iOS). I'm stuck on part c and d, don't know exactly what its asking me to do.
I know how to print int (%i) and object (%#), but %# print all data? Any help or suggestion will be appreciated.
Part 6
a) Implement class A with properties a1, a2, and a3 (int, string, int).
b) New objects are automatically initialized to 1, "hello", 1
c) Also provide initializer to any data and constructor (called without alloc) to do the same.
d) Make sure %# object of A will print all data.
Here is what I have done so far:
// classA.h
#import <Foundation/Foundation.h>
#interface ClassA : NSObject
// Part 6a
#property int a1;
#property NSString *a2;
#property int a3;
-(ClassA *) initWithA1: (int) x andA2: (NSString *) s andA3: (int) y;
#end
//classA.m
#import "ClassA.h"
#implementation ClassA
-(ClassA *) initWithA1:(int)x andA2:(NSString *)s andA3:(int)y {
self = [super init];
if (self) {
self.a1 = x;
self.a2 = s;
self.a3 = y;
}
return self;
}
// part 6b
-(ClassA *) init {
if (self = [super init]) {
self.a1 = 0;
self.a2 =#"hello";
self.a3 = 0;
}
return self;
}
#end
In reference to part "b" of your question:
As a general rule, only 1 initializer should be doing the "real" work. This is often referred to as the designated initializer. So, your init method should probably read something like:
- (id) init
{
return [self initWithA1:1 andA2:#"hello" andA3:1];
}
As #orbitor wrote, your class should have one designated initialiser.
So, your init method should probably read something like:
- (id) init
{
return [self initWithA1:1 andA2:#"hello" andA3:1];
}
In order to print all object you should implement custom description method:
- (NSString *) description
{
return [NSString stringWithFormat:#"a1 = %d, a2 = %#, a3 = %d", self.a1, self.a2, self.a3];;
}
According to c:
Class method new just calls alloc and init methods so you should only make sure that you wrote properly all initialisers.

global variable in objective C

I'm new in Objective C and stuck on this problem already 5 days)) What i have to do is write implementation for simple task about city and metropolis. I have class City with properties and class metropolis that has an global array which adds city object through createCity method. I have implemented this task but this arrays returns nothing.
Can anybody help me?
Here is part of the task:
1. Write a “City” class, which inherits from NSObject. Your class should contain the following:
Variables:
name, age, population.
Instance methods:
setName:age:population (single method) which set city’s name, age and population. getName, getAge, getPopulation which return city’s name, age and population, respectfully.
nextDay which adds a random number to city’s population, then subtracts a random number from city’s population. Figure out a way to generate random numbers yourself.
2. Create an instance of City class, set its name, age and population as you want.
3. Write a for-­‐loop (if in doubt how to do it – google or use Xcode’s help system) for 10 steps. Each step send ‘nextDay’ message to your object and print out the population.
4. Write a “Metropolis” class. It should contain the following:
Variable:
array of 10 cities.
Instance method:
createCity:atIndex:withPopulation: (single method) which creates a city with first parameter being a name at index (from the second parameter) and sets its population to that of third parameter. So, you should be able to do this:
[myMetropolis createCity: #”Almaty” atIndex: 2 withPopulation: 1500000]
5. Create an instance of Metropolis class and create all 10 cities.
Here is my implementation:
City.h
#import <Foundation/Foundation.h>
#interface City : NSObject
{
NSString* name;
int age;
int population;
}
-(void)setName: (NSString*)n age: (int)a population: (int)p;
-(NSString*)getName;
-(int)getAge;
-(int)getPopulation;
-(void)nextDay;
#end
City.m
#import "City.h"
#implementation City
-(void)setName:(NSString*)n age:(int)a population:(int)p
{
name = n;
age = a;
population = p;
}
-(NSString*)getName
{
return name;
}
-(int)getAge
{
return age;
}
-(int)getPopulation
{
return population;
}
-(void)nextDay
{
int r = arc4random() % 100;
int r2 = arc4random() % 100;
population = population + r;
population = population - r2;
}
#end
Metropolis.h
#import <Foundation/Foundation.h>
#import "City.h"
#interface Metropolis : NSObject{
NSMutableArray* myArray;
}
-(void)createCity: (NSString*)n atIndex: (int)a withPopulation: (int)p;
-(NSMutableArray*) getArray;
#end
Metropolis.m
#import "Metropolis.h"
#import "City.h"
#implementation Metropolis
NSMutableArray* myArray = nil;
- (void)initialize {
myArray = [[NSMutableArray alloc]initWithCapacity:10];
}
-(void)createCity:(NSString*)n atIndex:(int)a withPopulation:(int)p
{
City* newCity = [[City alloc]init];
[newCity setName:n age:0 population:p];
[myArray insertObject:newCity atIndex:a];
}
-(NSMutableArray*)getArray
{
return myArray;
}
#end
main.m
#import <Foundation/Foundation.h>
#import "City.h"
#import "Metropolis.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Metropolis* myMetropolis = [[Metropolis alloc]init];
[myMetropolis createCity:#"Aktobe" atIndex:0 withPopulation:15];
[Metropolis initialize];
NSMutableArray* c = [[NSMutableArray alloc]init];
c = [myMetropolis getArray];
NSLog(#"%#", [[c objectAtIndex:0] getName]);
}
return 0;
}
The method for initialization is -(void)init; this method should be overwritten in your implementation of Metropolis.
You are calling - (void)initialize; which is wrong in this case.
So, simply change - (void)initialize { to -(void)init { in your implementation of Metropolis and delete the line: [Metropolis initialize]; in main.
After the comment below the proper init method should be:
-(id) init {
self = [super init];
if (self) {
myArray = [[NSMutableArray alloc]initWithCapacity:10];
}
return self;
}
I've rewritten my answer to make it more complete, and to incorporate some of the other ideas generated in the other answers, especially #Hannes Sverrisson
The easy way to fix your issue is to call initialize BEFORE createCity (otherwise your trying to add objects to a nil array) and to also make sure you're not calling initialize from a static context. i.e. change [Metropolis initialize]; to [myMetropolis initialize];
The better way, and by better I mean more consistent with typical objective-c design, you should override the instance method init. This is done in the Metropolis implementation and replaces your initialize method.
-(id) init {
self = [super init];
if (self) {
myArray = [[NSMutableArray alloc]initWithCapacity:10];
}
return self;
}
or to make it more fun, create a new init method that takes the number of cities as a parameter.
-(id) initWithNumberOfCities:(NSInteger)numCities {
self = [super init];
if (self) {
myArray = [[NSMutableArray alloc]initWithCapacity:numCities];
}
return self;
}
Then in your main method, remove the call to [Metropolis initialize]. The reason for this is when you say:
Metropolis* myMetropolis = [[Metropolis alloc]init];
or
Metropolis* myMetropolis = [[Metropolis alloc]initWithNumberOfCities:10];
the init method is being called inline after the allocation takes place.
You don't need to write getters or create backing instance variables. You can use Objective-C 2.0's #property syntax.
#property (strong) NSString *name;
#property (assign) NSInteger age;
#property (assign) NSInteger population;
- (void)setName:(NSString*)name age:(NSInteger)age population:(NSInteger)population;
- (void)nextDay;
Then you access the properties using self.name, self.age, self.population or if you need to access the backing variable itself, _name, _age, _population.

"Expected a type" error Objective C

I've asked questions on here so many times about this ruddy game that I'm trying to make. I'm working on a Text-Based adventure game. First I made it in Java because that's what I was learning the the class the game was for. Now I'm trying to learn iOS development which requires objective-c. I feel pretty comfortable with objective c after taking the Lynda Essentials course (The previous experience with Java helped of course). Anyways I'm working on this game and I'm running into a problem that seems pretty unique to objective c.
In Java when I have multiple classes they just need to be in the same directory in order for me to use them in other classes. This is not the case in Objective-C... I have to import the header files if I want to use class A in class B. Well for this game I have two custom classes, a Location class and an Exit class. The Location class needs to know about what Exits it has (So I have to import Exit.h if I want to use them) and the exits need to know which location it's connected to (So I have to import Location.h). It seems that I can't do this because of something called Circular Referencing (or something like that). However, if I don't do this then I get an "Expected a type" error. So I have no idea what to do. I'll show the code below.
Exit.h
#import <Foundation/Foundation.h>
#import "Location.h"
#define NORTH 0
#define SOUTH 1
#define EAST 2
#define WEST 3
#interface Exit : NSObject
#property NSString * dirName;
#property NSString * dirShortName;
#property int direction;
#property Location * connection;
-(id)initWithConnection:(Location *) loc andDirection:(int) dir;
#end
Exit.m
#import "Exit.h"
#implementation Exit
#synthesize dirName;
#synthesize dirShortName;
#synthesize direction;
#synthesize connection;
-(id)initWithConnection:(Location *)loc andDirection:(int)dir {
self = [super init];
if(self) {
direction = dir;
switch(direction) {
case 0:
dirName = #"North";
dirShortName = #"N";
break;
case 1:
dirName = #"South";
dirShortName = #"S";
break;
case 2:
dirName = #"East";
dirShortName = #"E";
break;
case 3:
dirName = #"West";
dirShortName = #"W";
break;
}
connection = loc;
}
return self;
}
#end
Location.h
#import <Foundation/Foundation.h>
#interface Location : NSObject
#property NSString * title;
#property NSString * desc;
#property NSMutableDictionary * exits;
#property BOOL final;
-(id) initWithTitle:(NSString *) _title;
-(id) initWithDescription:(NSString *) _desc;
-(id) initWithTitle:(NSString *) _title andDescription:(NSString *) _desc;
-(void) addExit:(Exit *) _exit;
#end
Location.m
#import "Location.h"
#implementation Location
#synthesize title;
#synthesize desc;
#synthesize exits;
#synthesize final;
-(void) addExit:(Exit *) _exit {
NSString * tmpName = [_exit dirName];
NSString * tmpShortName = [_exit dirShortName];
[exits setObject:tmpName forKey:tmpShortName];
}
-(NSString *)description {
NSString * tmp = [[NSString alloc] initWithFormat:#"%#\n%#\n",self.title,self.desc];
for(NSString * s in exits) {
[tmp stringByAppendingFormat:#"\n%#",s];
}
return tmp;
}
// Initialization Methods
-(id) init {
self = [super init];
if(self) {
title = #"";
desc = #"";
}
return self;
}
-(id) initWithTitle:(NSString *) _title {
self = [super init];
if(self) {
title = title;
desc = #"";
exits = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil];
}
return self;
}
-(id) initWithDescription:(NSString *) _desc {
self = [super init];
if(self) {
title = #"";
desc = desc;
exits = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil];
}
return self;
}
-(id)initWithTitle:(NSString *) _title andDescription:(NSString *)_desc {
self = [super init];
if(self) {
title = title;
desc = desc;
exits = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil];
}
return self;
}
#end
I'm really hoping I'm not trying to do something that's impossible. I also hope my code can be made sense of and I'm not making too much of a fool of myself here ;) thanks for any advice.
EDIT:
Just reread and now understand better, you need to do #class Exit; to define the Exit class in the Location header and then you can do the same #class Location; in the Exit header in order to tell the compiler that the classes are defined. Then if you were to reference those classes in the implementation files (.m) then you would import the Exit.h file and Location.h file respectively
The rule of thumb I have started to follow, which seemed counter-intuitive to me at first is this:
In your header files, use "forward declarations" prolifically with only 2 exceptions:
headers for classes you are extending, and headers for protocols you are conforming to; and only do #import directives in your .m files.
This should resolve the circular reference error; it did mine.
See here, and do a 'find' for the word "forward".

Potential problems in objective-c code

Here is a small piece of code. Posted by Russian company Yandex as a part of their interview. What are potential problems here? It looks very simple, should be hidden problems I can not see.
First header
//Foo.h
#import <Cocoa/Cocoa.h>
#interface Foo : NSObject
{
NSString* str;
static int i = 0;
}
- (NSString*) str;
#end
Another file
//Foo.m
#import "Foo.h"
#implementation
- (id) init
{
return [self initWithStr:"number:" someInt:6];
}
- (id) initWithStr:(NSString*)theStr someInt:(int)value
{
self = [super init];
str = [NSString stringWithFormat:#"%#%d", theStr, value];
return self;
}
- (NSString*) str
{
return str;
}
- (void) setStr:(NSString*)theStr
{
str = theStr;
}
#end
And the last file
//main.m
#import <Cocoa/Cocoa.h>
#import "Foo.h"
int main(int argc, char *argv[])
{
Foo objA;
NSLog([objA str]);
[objA setStr:#"hello world!"];
NSLog([objA str]);
Foo* objB = [[Foo alloc] init];
Foo* objC = [[Foo alloc] initWithStr:#"My magic number:" value:265];
objB = objC;
NSLog([objB str]);
[objA release];
[objB release];
[objC release];
return 0;
}
In another file:
#implementation
implementation of what? must specify.
In the last file:
Foo objA;
NSLog([objA str]);
[objA setStr:#"hello world!"];
NSLog([objA str]);
This will crash, local variable Foo objA is not initialized, it would be fine it was set to nil, since messages to nil are ok in objective c but it is not.
Here:
[objA setStr:#"hello world!"];
That method will give a compile warning since that method is not declared in the interface, but it will still call the method.
Here:
- (id) init
{
return [self initWithStr:"number:" someInt:6];
}
Missing # for the string #"number:"
Here:
objB = objC;
You just leaked objB, since there is now no valid reference to release the previous allocation.
[objA release];
This was never allocated!
[objB release];
[objC release];
The second one will crash since they both refer to the same object, and the retain count is only 1.
The first file also has some potential issues such as declaring a method that appears to be a getter without declaring a property for the ivar, same with the setter, would be better to just declare a property.
#interface Foo : NSObject
{
NSString* str;
static int i = 0;
}
You cann't define static int i = 0; here. Type name does not allow storage class to be specified Foo.h
Also, the setter needs to release the previous string and retain the new one.
- (void) setStr:(NSString*)theStr
{
if(str) {
[str release];
}
str = [theStr retain];
}

Giving each subclass its own copy of a class variable

I have the following class in my iOS application (it is like an abstract class from the Java world).
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return _dictionary;
}
...
#end
I then have multiple classes which implement this above WSObject with the class method dictionary. The problem is, that each of these classes should have their own _dictionary, but they are all sharing the same object from the super class. I could, of course, copy to all the subclasses, but that would break the reusability. Besides this getter, there are other class methods in WSObject which mutate the dictionary. Because of this, there would be a several class methods which should be in every subclass.
How can I solve this in a smart way? Please tell me if my description is insufficient.
Associative references seem like they'll do the trick. You can essentially tack some storage on to the class object itself. (I'm using NSStrings here, in place of the dictionaries you want to use, just for demonstration.)
Superclass:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Stuper : NSObject
// Accessor method for the "class variable"
+ (NSString *) str;
// Analog to your +localStorePath
+ (NSString *) quote;
#end
#import "Stuper.h"
// The doc suggests simply using the address of a static variable as the key.
// This works fine, even though every class is (as in your problem) using
// the same key, because we are associating to a different class each time.
static char key;
#implementation Stuper
+ (NSString *) str {
NSString * s = objc_getAssociatedObject(self, &key);
if( !s ){
s = [self quote];
// You'll probably want to use OBJC_ASSOCIATION_RETAIN for your dictionary.
// self inside a class method is the class object; use that as
// the associator. The string is now tied to the associator, i.e.,
// has the same lifetime.
objc_setAssociatedObject(self, &key, s, OBJC_ASSOCIATION_COPY);
}
return s;
}
+ (NSString *) quote {
return #"It was the best of times, it was the worst of times.";
}
#end
Subclass:
#import "Stuper.h"
#interface Stub : Stuper #end
#import "Stub.h"
#implementation Stub
+ (NSString *) quote {
return #"Call me Ishmael.";
}
#end
Trying this out:
#import <Foundation/Foundation.h>
#import "Stuper.h"
#import "Stub.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"%#", [Stuper str]);
NSLog(#"%#", [Stub str]);
[pool drain];
return 0;
}
Each class object now has its own string, associated with it.
2011-12-05 23:11:09.031 SubClassVariables[36254:903] It was the best of times, it was the worst of times.
2011-12-05 23:11:09.034 SubClassVariables[36254:903] Call me Ishmael.
The only downside here is that you'll have to call the accessor method every time you want the object; you don't have a pointer you can use directly. You can call objc_getAssociatedObject in the superclass as an accessor, too, of course, since it has access to key.
In order to give each subclass its own dictionary, store a second dictionary object in your primary dictionary using the class name as the key. For example:
static NSMutableDictionary *_dictionary = nil;
+ (NSDictionary*)dictionary
{
if (_dictionary == nil)
_dictionary = [[NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]] mutableCopy];
NSString *key = NSStringFromClass( [self class] );
if ( [_dictionary objectForKey:key] == nil )
[_dictionary setObject:[NSMutableDictionary dictionary] forKey:key];
return [_dictionary objectForKey:key];
}
Perhaps you can return a copy of the dictionary
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return [_dictionary copy];
}
...
#end
Keep in mind that if you modify _dictionary you will get a copy of that modified dictionary which may differ from what is on disk.
How often is this being called? is it really necessary to cache the file contents in this static _dictionary object?
Why not just fetch it every time form disk, assuming it isn't too often that performance comes into question.
#implementation WSObject
+(NSDictionary*) dictionary {
return [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
...
#end