How To Call Methods In Objective-C - objective-c

I'm having an issue calling methods in a fairly simple script.
For some reason, when I run the program, all I get in the console is return 0.
What can I do to fix it? Here's my script:
#import <Foundation/Foundation.h>
#interface person:NSObject {
int weight;
int height;
int age;
}
- (void)printtoscreen;
- (void)setweight:(int)w;
- (void)setheight:(int)h;
- (void)setage:(int)a;
#end
#implementation person
-(void) printtoscreen {
NSLog(#"I am %i years old, I weigh %i pounds, and I am %i feet tall", age, weight, height);
}
- (void)setweight:(int)w {
w=weight;
}
- (void)setheight:(int)h {
h=height;
}
- (void)setage:(int)a {
a=age;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
person *bob;
[bob setweight:150];
[bob setheight:5];
[bob setage:25];
[bob printtoscreen];
}
return 0;
}

The biggest problem is that we never actually create an object, so all of our messages are sent to nil.
The first and most immediate fix you must make is actually instantiating the object:
person *bob = [[person alloc] init];
This must be done before any of our methods are actually called on an object.
Now, we must fix the methods that Jens mentioned. Our assignments our backwards. The argument should be the right-value and the instance variable should be the left-value:
- (void)setweight:(int)w {
weight = w;
}
With these two fixes, all should work.

Your setter are totaly wrong:
-(void) setweight: (int) w {
w=weight;
}
should be:
-(void) setweight: (int) w {
weight = w;
}
And so on for the other setter

Related

Classes in Objective-C

I have a class named person, with two values: age and weight.Why can't I access these two values in the main function like this:
int a=[chuck age];
int b=[chuck weight];
What is the best way to do that? Using properties is the correct way?
Header file:
#import <Foundation/Foundation.h>
#interface person : NSObject
{
int age;
int weight;
}
-(void) print;
-(void) setAge;
-(void) setWeight;
#end
Implementation file:
#import "person.h"
#implementation person
-(void) print
{
printf("Your age is %d and your weight is %d.", age, weight);
}
-(void) setAge
{
printf("Write age: ");
int v;
scanf("%d", &v);
age=v;
}
-(void) setWeight
{
printf("Write weight: ");
int g;
scanf("%d", &g);
weight=g;
}
#end
Are you working from some kind of a tutorial or book? That is an odd place to start for learning to write OS X or iOS apps.
In any case, the issue is that you've colluded getter/setter stuff with methods that implement other functionality.
I would suggest that your Person class be declared as:
#interface Person : NSObject
#property NSInteger age;
#property NSInteger weight;
#end
With Person.m:
#implementation Person
- (id) init {
self = [super init];
if (self) {
// preposterous initial values so we know if they weren't set.
_age = -1;
_weight = -1;
}
return self;
}
#end
That is, a Person only holds information about a single person. It does not do any kind of I/O, etc...
Then, your main.m would look something like:
#import <Foundation/Foundation.h>
#import "Person.h"
NSInteger ScanIntegerWithPrompt(NSString *prompt) {
printf("%s: ", [prompt UTF8String]);
int v;
scanf("%d", &v);
return (NSInteger) v;
}
int main(...) {
#autoreleasepool {
Person *p = [[Person alloc] init];
p.age = ScanIntegerWithPrompt(#"Enter age:");
p.weight = ScanIntegerWithPrompt(#"Enter weight:");
printf("Your age is %d and your weight is %d", p.age, p.weight);
}
return 0;
}
Structuring the code this way separates the Model -- the data container -- from the Control layer. There isn't much of a View layer here.
If you really wanted to keep the I/O / parse logic with the Person object, then add something like this to the Person object:
...
- (NSNumber)readIntegerWithPrompt:(NSString*)prompt
{
... same code as function above ...
}
- (void)readAgeFromStandardInput
{
self.age = [self readIntegerWithPrompt:#"Enter age: "];
}
- (void)readWeightFromStandardInput
{
self.weight = [self readIntegerWithPrompt:#"Enter weight: "];
}
...
Then you'd call those methods from your main.
Your problem is You're trying to access private age and weight ivars, which aren't accessible this way.
The good way to do this is to use ObjC properties, but this is not required for your example.
You need to create two methods to access the private ivars, call them age and weight, they should look like this in the class interface:
- (int) age;
- (int) weight;
and the implementation is:
- (int) age{
return age;
}
- (int) weight{
return weight;
}
Now in your main.m you can easily access the data needed like this:
#import <Foundation/Foundation.h>
#import "person.h"
int main(int argc, char *argV[]) {
#autoreleasepool {
person *andrew = [[person alloc]init];
[andrew setAge];
[andrew setWeight];
NSLog(#"Age:%d, Weight:%d",[andrew age], [andrew weight]);
}
return 0;
}
If you want to know how it's done with properties please let me know and I can update the answer :)
In your header file :
#property int age;
#property int weight;

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.

Making pointer from integer in Objective C

After programming with C# and Java for many years I finally decided to learn Objective-C in order to start programming iOS Devices as well as Mac OS X, and I have to admit it is very different then most modern c-based programming languages. I am getting the following warning in my code:
warning: passing argument 1 of 'SetAge' makes pointer from integer without a cast
Here is my code:
Dog.h
#import <Cocoa/Cocoa.h>
#interface Dog : NSObject {
int ciAge;
NSString * csName;
}
- (void) Bark;
- (void) SetAge: (int *) liAge;
- (void) SetName: (NSString *) lsName;
#end
Dog.m
#import "Dog.h"
#implementation Dog
- (void) Bark
{
NSLog(#"The dog %# barks with age %d", csName, ciAge);
}
- (void) SetAge: (int *) liAge {
ciAge = (int)liAge;
}
- (void) SetName: (NSString *) lsName {
csName = lsName;
}
#end
HelloWorld.m
#import <Foundation/Foundation.h>
#import "Dog.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int liTemp = 75;
NSString * lsCity = #"Chicago";
NSDate * loDate = [NSDate date];
// insert code here...
NSLog(#"The temperature is %d currently in %#, on %#", liTemp, lsCity, loDate);
int liAge = 10;
// Call Dog class
Dog * Dog1 = [Dog new];
[Dog1 SetAge:(int)liAge]; // The Warning happens here
[Dog1 SetName:#"Fido"];
[Dog1 Bark];
[pool drain];
return 0;
}
My Questions Are:
How do I get rid of the warning above?
Instead of creating methods in the Dog Class for setting Age and Name, how could I have make Age and Name public class level variables that I could directly assign to?
Any help would be much appreciated!
Thanks,
Pete
Don't declare an int as a pointer. Change your code from:
- (void) SetAge: (int *) liAge
to
- (void) SetAge: (int) liAge
and
- (void) SetAge: (int *) liAge {
ciAge = (int)liAge;
}
to
- (void) SetAge: (int) liAge {
ciAge = liAge;
}
Consider making age and name a property. Change:
- (void) SetAge: (int *) liAge;
- (void) SetName: (NSString *) lsName;
to
#property (nonatomic, readwrite) NSInteger age; //Don't declare as pointer
#property (nonatomic, retain) NSString *name; //DO declare as pointer
Also, don't forget to synthesize them in your implementation file:
#synthesize age, name;
int is a C primitive scalar type so you don't need pointers. Don't use *.
Warning here
int * represents a pointer variable, a thing you're not used to see in C# or in Java.
int *p is a variable that will point to a memory address. To put data at this address you have to dereference the variable p before using it ex:*p = 3.
Objective-C is based on the C language and this is a C language problem. You should (must ?) read about C and pointers if you want to code in Objective-C.
And read also how Objective-C simplifies your life with pointers to objects particularly the fact that you don't have do explicitly dereference them to use them.

static behaviour->objective C

I have this static declaration of m_pData=1099656 in a class.
In main NSLog(#"Testing:%d",m_pData);
Its printing me 0.
How to get the same value of m_pData in the class in the main function too.
I cant use obj.m_pData or obj->m_pData to acces the variable.
EDITED:
test2.m
----------
#import <Foundation/Foundation.h>
#import "data_derived.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
data* dat = [data alloc];
requestSession* session = [requestSession alloc];
[session init];
[dat TxCreateImage:RM_REQUEST_SESSION];
NSLog(#"Testing:%d",m_pData); //the static variable is not printing the value its holding.Its printing Zero.If printed the same variable inside the class it gives some numbers.
[dat dataBuffer:&m_pData withLen:&m_uDataSize]; //here the actual values of static values are not passed.when printed both of them contains zero values.
[pool drain];
return 0;
}
data.h
--------
#import <Foundation/Foundation.h>
#import "remote.h"
static int m_nMessageId; //Message ID
static int m_uSessionId; //Session ID
static int m_chSequenceChar; //Sequence ID
static int* m_pData; //Integer buffer to carry data
static int m_uDataSize; //Datasize
#interface data : NSObject {
#public
}
- (id)initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize;
+ (void)initialize;
- (void)dealloc;
- (id) dataBuffer:(int**)m_Data withLen:(int**)uLen;
- (BOOL) TxCreateImage:(int)messageId;
#end
data.m
---------
#import "data.h"
#define ENCODED_MSG_DATA_OFFSET 8
#implementation data
+ (void)initialize
{
m_uSessionId = 0;
m_chSequenceChar= 0;
// Initialize values from derived class
m_nMessageId = 0;
m_pData = 0;
m_uDataSize = 0;
}
- (id) initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize
{
if(self=[super init])
{
// Initialize the member variables
m_uSessionId = 0xFF;
m_chSequenceChar= 10;
// Initialize values from derived class
m_nMessageId = uMessageId;
m_pData = (int*)pData;
m_uDataSize = (int)uDataSize;
}
NSLog(#"Data size:%d",uDataSize);
NSLog(#"m_pData:%d",m_pData);
NSLog(#"pData:%d",pData);
return self;
}
- (id) dataBuffer:(int**)m_Data withLen:(int**)uLen
{
if ( m_uDataSize <= RMH_MAX_ENCODED_LENGTH )
{
int abBigEndian[RMH_MESSAGE_MAX_SIZE];
memcpy(abBigEndian,m_Data,m_uDataSize);
NSLog(#"m_Data:%d",*m_Data);
NSLog(#"abBigEndian:%d",abBigEndian);
uLen += ENCODED_CRC_BYTE_LEN + 1;
NSLog(#"%d",*uLen);
}
NSLog(#"END!");
return self;
}
- (BOOL) TxCreateImage:(int)messageId
{
char pData[4096];
sprintf(pData,"%x %d %d %d %x",ASCII_STX,m_uSessionId,m_chSequenceChar,m_nMessageId,ASCII_ETX); //uLen = ENCODED_MSG_DATA_OFFSET;
NSLog(#"%s",pData);
return YES;
}
- (void)dealloc
{
[super dealloc];
}
#end
data_derived.h
---------------------
#import <Foundation/Foundation.h>
#import "data.h"
#define DECLARE_RS232_NEWMSG(ClassID)\
enum \
{ \
ID = ClassID \
}; \
#interface requestSession : data {
#public
DECLARE_RS232_NEWMSG(RM_REQUEST_SESSION);
struct RMH_REQUEST_SESSION_MSG st;
}
-(id)init;
-(void)dealloc;
#end
data_derived.m
---------------------
#import "data_derived.h"
#implementation requestSession
- (id)init
{
size_t asize = sizeof(st);
st.uDeviceID = RS232_PROTOCOL_DEVICE_ID;
st.uProtocolVersion = RS232_VERSION;
memset(st.uReserved,0x00,sizeof(st.uReserved));
NSLog(#"Address of the structure:%d",&st);
self=[super initWithID:ID withData:(id)&st withSize:asize];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#end
Don't place your static members in .h file. Move them to data.m.
Also about static keyword - in C (and Objective C, which is clear superset to C), when using static with global variables (like you do), it only indicates that this variables will be local to the file you declared these variable in. So global variables anyway have only one instance, with static modifier or without. If you don't use static, then you can access these variables from other files with external declaration, like:
// file1.m
int variable = 4;
// file2.m
external int variable; // variable == 4
That's why your code is printing you 0. m_pData in test2.m is not the same m_pData you have in data.m. Without static modifier you would get a linker error.
And you might want to write getters/setters for static members. Like:
+ (int *)pData {
return m_pData; // or smth like memcpy
}

Obj-c method override/polymorphism problem

Ok, so I'm using Objective-C. Now, say I have:
TopClass : NSObject
- (int) getVal {return 1;}
MidClass : TopClass
- (int) getVal {return 2;}
BotClass : MidClass
- (int) getVal {return 3;}
I then put objects of each type into an NSMutableArray and take one out. What I want to do is run the getVal func on the appropriate object type, but when I put
id a = [allObjects objectAtIndex:0];
if ([a isKindOfClass:[TopClass class]])
{
int i;
i = [a getVal];
}
I get firstly a warning about multiple methods called getVal (presumably because the compiler can't determine the actual object type until runtime). But more seriously I also get an error "void value not ignored as it should be" and it won't compile.
If I don't try and use the return from [a getVal] then it compiles fine e.g.
[a getval]; //obviously no good if I want to use the return value
It will also work if I use isMemberOfClass statements to cast the object to a class before running the function e.g.
if ([a isMemberOfClass:[BotClass]) i = [(BotClass*) a getVal];
But surely I shouldn't have to do this to get the functionality I require? Otherwise I'll have to put in a statement for every single subclass, and worse have to add a new line if I add a new sub class, which rather defeats the point of method overriding doesn't it?
Surely there is a better way?
Since a BotClass is a MidClass and a MidClass is a TopClass, you could just set the type of a to TopClass*.
TopClass* a = [allObjects objectAtIndex:0];
if ([a isKindOfClass:[TopClass class]]) {
int i;
i = [a getVal];
}
The better way is to add -getVal to the #interface and #import it. Then the compiler will know such this method is likely to return an int and won't complain even if a is an id. Make sure the method name won't coincide with others, though.
(BTW, in ObjC, getters won't be named as -getFoo. The convention is just call it -foo.)
Your code compiles without any warning:
#import <Foundation/Foundation.h>
#interface TopClass : NSObject
{
}
- (int) getVal;
#end
#interface MidClass : TopClass
{
}
- (int) getVal;
#end
#interface BotClass : MidClass
{
}
- (int) getVal;
#end
#implementation TopClass
- (int) getVal {
return 1;
}
#end
#implementation MidClass
- (int) getVal
{
return 2;
}
#end
#implementation BotClass
- (int) getVal
{
return 3;
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *array = [NSMutableArray array];
[array addObject:[[[TopClass alloc]init]autorelease]];
[array addObject:[[[MidClass alloc]init]autorelease]];
[array addObject:[[[BotClass alloc]init]autorelease]];
for (int objectNumber = 0; objectNumber < [array count]; objectNumber++) {
id a = [array objectAtIndex:objectNumber];
if ([a isKindOfClass:[TopClass class]])
{
int i = [a getVal];
NSLog(#"%d",i);
}
}
[pool drain];
return 0;
}
The test "isKindOfClass" is always true in this case, as MidClass and BotClass inherit from TopClass.
Are you sure the compiler knows about the method signatures that you call?