global variable in objective C - 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.

Related

Why method is not getting called

I'm trying to learn how to make a class and object and how to call methods in Objective-C. My small program creates an object of the City class, allows to name that object, set an age, population, and get these values to print. But when I call a method to set these values, I get a (null) and zeros in result. Here's my code:
City.h
#import <Foundation/Foundation.h>
#interface City : NSObject
-(void) setName:(NSString *)theName Age:(int)theAge Population:(int)thePopulation;
-(void) getName;
-(void) getAge;
-(void) getPopulation;
-(void) nextDay;
#end
City.m
#import "City.h"
#implementation City
{
NSString *name;
int age;
int population;
}
-(void) setName:(NSString *)theName Age:(int)theAge Population:(int)thePopulation
{
theName = name;
theAge = age;
thePopulation = population;
}
-(void) getName
{
NSLog(#"Name is %#", name);
}
-(void) getAge
{
NSLog(#"Age is %d", age);
}
-(void) getPopulation
{
NSLog(#"Population today is %d", population);
}
main.m
int main()
{
City *moscow = [[City alloc] init];
[moscow setName:#"Msk" Age:120 Population:1000];
[moscow getName];
[moscow getAge];
[moscow getPopulation];
}
the result of the running is:
Name is (null)
Age is 0
Population today is 0
Program ended with exit code: 0
what am I doing wrong?
The problem is that the instance variables of City is never set. The code in setName:Age:Population: assigns the values of the instance variables (name, age, and population) to the arguments variables (theName, theAge, and thePopulation). Swapping these will cause the setter to assign the arguments to the instance variables:
name = theName;
age = theAge;
population = thePopulation;
That said, it's more idiomatic Objective-C to use properties—instead of instance variables and manual getters and setters—and to use an initializer to set the initial values. With those changes the City class would look something like this:
City.h
NS_ASSUME_NONNULL_BEGIN
#interface City : NSObject
#property (copy) NSString *name;
#property (assign) NSInteger age;
#property (assign) NSInteger population;
- (instancetype)initWithName:(NSString *)name
age:(NSInteger)age
population:(NSInteger)population;
#end
NS_ASSUME_NONNULL_END
City.m
#import "City.h"
#implementation City
- (instancetype)initWithName:(NSString *)name
age:(NSInteger)age
population:(NSInteger)population
{
self = [super init];
if (self) {
_name = [name copy];
_age = age;
_population = population;
}
return self;
}
#end
Two things to note about this code:
The string is copied—both in the initializer and in the property—to protect against having a NSMutableString is passed and later be mutated (which would mutate the value of name as well. For the common case where an immutable NSString is passed, the copy is equivalent to a "retain".
The synthesized instance variables are used when assigning values in the initializer. This is to protect against having a subclass override any of these properties and have a custom setter method run before the object is fully initialized (have all its variables set to their initial values). This only applies to initializers, custom setters, and dealloc. Everything else should use the properties to access and modify these values.

Objective C getter method not working for property

I was playing around with objective C. This is my code for a class I wrote , arithmetic.h:
#import <Foundation/Foundation.h>
#interface arithmetic : NSObject
#property int cur;
-(id)initWithNumber:(int)number;
#end
#implementation arithmetic
#synthesize cur;
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(#"Yo, all works :D ");
}
return self;
}
-(id)initWithNumber:(int)num{
self = [super init];
if(self){
[self setCur:8] ;
}
return self;
}
#end
Note the #property int cur. I was excepting objective c to create a setCur and a getCur method as accessors and mutators for my class. However, when I run this:
arithmetic *test = [[arithmetic alloc] initWithNumber:89];
[test setCur:534];
NSLog("%i",[test getCur ]);
The first two lines work. But the last line says
No visible interface for arithmetic declares the selector 'getCur'
What is the problem ?
It is because when you declare like this in your #implementation:
#synthesize cur;
it will create getter
-(int)cur {
return _cur;
}
and also it will create a setter
-(void)setCur:(int)newCur {
_cur = newCur;
}
In summary, Objective-C getter/setter is having a pattern of propery/setPropery respectively, unlike Java that uses getProperty/setProperty.
And Objective-C getter/setter is accessed via dot(.) notation. For example
int x = obj.cur;
obj.cur = 100;

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.

"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".

NSCopying & Inheritance

Please have a look on following code :------- .h
#interface BankAccount : NSObject<NSCopying>
{
double accountBalance;
long accountNumber;
NSString *CustomerName;
NSString *AccountType;
}
-(void) setAccount: (long) y andBalance: (double) x;
-(void) setCustomerName: (NSString*) name andAccountType: (NSString*) type;
-(id)copyWithZone:(NSZone *)zone;
#end
#interface Savings : BankAccount
{
int number;
NSString *Offer;
}
-(void) setSavingNumber: (uint8_t) num andOffer: (NSString*) offer;
-(id)copyWithZone:(NSZone *)zone;
#end
---------- .m
#implementation BankAccount
-(void) setAccount: (long) y andBalance: (double) x
{
accountNumber = y;
accountBalance = x;
}
-(void) setCustomerName: (NSString*) name andAccountType: (NSString*) type
{
CustomerName = name;
AccountType = type;
}
-(id)copyWithZone:(NSZone *)zone
{
BankAccount *accountCopy = [[BankAccount allocWithZone: zone] init];
[accountCopy setAccount: accountNumber andBalance: accountBalance];
[accountCopy setCustomerName:CustomerName andAccountType:AccountType];
return accountCopy;
}
#end
#implementation Savings
-(void) setSavingNumber: (uint8_t) num andOffer: (NSString*) offer
{
number = num;
Offer = offer;
}
-(id)copyWithZone:(NSZone *)zone
{
Savings * clone = [super copyWithZone:zone];
[clone setSavingNumber:number andOffer:Offer];************** error *********
return clone;
}
#end
When run this code::::::
Savings* account1;
Savings* account2;
account1 = [[Savings alloc] init];
[account1 setAccount:10 andBalance:1000.10];
[account1 setCustomerName:[NSString stringWithFormat:#"%#",#"Deepak"] andAccountType:[NSString stringWithFormat:#"%#",#"Savings"]];
[account1 setSavingNumber:2001 andOffer:#"Bad"];
account2 = [account1 copy];
#
i dont know what is wrong with the code please help me. thanks in advance.
Thanks
Deepak
Your base copyWithZone: method should look like this:
-(id)copyWithZone:(NSZone *)zone {
// change this line to use [self class]
BaseClass *base = [[[self class] allocWithZone:zone] init];
// copy base members:
// ...
return base;
}
Your derived copyWithZone: methods should look like this:
-(id)copyWithZone:(NSZone *)zone {
DerivedClass *derived = [super copyWithZone:zone];
// copy derived members:
// ...
return derived;
}
Also make sure sure you are making deep copies of strong references and shallow copies of weak references. Thus to for example copy members of type NSString and NSArray (each one with strongly referenced members and one with weak) do:
derived.strongString = [[strongString copyWithZone:zone] autorelease];
derived.weakString = weakString;
derived.strArrWStrVals = [[strArrWStrVals copyWithZone:zone] autorelease];
derived.strArrWWeakVals = [[[NSArray allocWithZone:zone]
initWithArray:strArrWWeakVals] autorelease];
derived.weakArray = weakArray;
(Usually weak members are also assigned/retained and strong variables are copied.)
Take note that because of this you should not use initWithMyself: type methods to copy data.
Ok first, your test code is bad because you set a saving number of 2001 in an 8 bit integer. Second, your code does not run because you're trying to call setSavingNumber:andOffer: on a BankAccount instead of an Saving object so it can't find the selector for this method at run time. Thanks David for pointing that out.
When implementing BankAccount::copyWithZone, you used the alloc-init and you returned the object which is fine. When implementing Savings::copyWithZone, you call super copyWithZone. What you get is an object of type BankAccount so you cannot use setSavingNumber:andOffer: on it. Since you used the alloc-init in the base class, you should also use the alloc-init and setMethods in the Savings class.
To avoid duplicating code, I'd recomment implementing an initWithBankAccount in BankAccount and the same in Savings.
Then in the copyWithZone, you'd have
return [[BankAccount allocWithZone:zone] initWithBankAccount:self];
and
return [[Savings allocWithZone:zone] initWithSavings:self];
Although you must make sure that in initWithSavings you call either
self = [super initWithBankAccount:savings];
or you call straight init and you copy the base member initialization.
Have a look at Implementing object copy from Memory management programming guide.