Property and Synthesize in Cocoa - objective-c

I am trying to learn Cocoa and wanted to develop a very simple game that I had played in my youth. It was originally created in MS Basic. Please note I'm very new to Cocoa and objective c
I have create and object called Defaults. It stores three integer variables, planets, fighters and players.
On the front end, I have three drop down boxes with various defaulted values.
I wanted to understand KVC as well as how to set values but I get the following warning...
"Incompatible integer to pointer sending 'int' to parameter of type 'int *'
[NEW CODE]
DEFAULTS.H
#import
#import
#interface Defaults : NSObject {
int fighters;
int planets;
int players;
}
#property (assign) int fighters;
#property (assign) int planets;
#property (assign) int players;
#end
Here is an excerpt of the implementation in my controller:
#import "DefaultsController.h"
#import "Defaults.h"
#implementation DefaultsController
- (id)init
{
self = [super init];
if (self) {
// Initialization code here
Defaults *theDefaults = [[Defaults alloc] init];
NSLog(#"Planets: %#",[theDefaults valueForKey:(#"planets")]);
}
return self;
}
-(void)dealloc
{
[super dealloc];
[Defaults release];
}
-(void) awakeFromNib{
}
-(IBAction) addPlanets:(id)sender{
[theDefaults setValue:[planetsButton titleOfSelectedItem] forKey:#"planets"];
[theDefaults setPlanets:[[planetsButton titleOfSelectedItem] intValue]];
theDefaults.planets = [[planetsButton titleOfSelectedItem] intValue];
NSLog(#"Planets: %i", theDefaults.planets);
NSLog(#"%#", [planetsButton titleOfSelectedItem]);
NSLog(#"%i", [[planetsButton titleOfSelectedItem] intValue]);
}
What am I doing wrong?

You've declared your properties to be integer pointers, but then you're attempting to set them to integer values, not integer pointer values. Remove the asterisks in "int *fighters", etc, to change them to integers.
Pointers are one of the most fundamental and challenging concepts of C-based languages (including Objective-C), and if you aren't clear on the distinction I drew above I suggest you look for some careful explanations of how they work.
Update:
Another issue I see here, which may relate to your question below, is that you're doing some funny things with your logging statements. When you put %# in a formatting string, you're telling NSLog that you want it to replace that with an Objective-C object. But an int is not an object; it's just a basic C type. Then, you're using key-value coding to retrieve your property value, which is further confusing the issue, because key-value coding is also intended to work with Objective-C objects. Replace that line with the following:
NSLog(#"Planets: %i", theDefaults.planets);
and I think you'll get something more in line with what you're expecting. %i tells NSLog that you have an integer value to print, and theDefaults.planets (or the equivalent [theDefaults planets]) will return the value of the planets property, whether it's an object or not.

Related

How to get Class Objects to automatically display behaviour

I'm having trouble understanding a finer point of Writing and calling Classes. It's probably
easlier to grasp in Swift but it bothers me to start that study without
getting it right in obj_c first. Currently I do everything in the
ViewControllers with iVars and Globals. With two apps 18 months in the App
store its overdue to put them right.
I've formed a notion that properties are the Object's State, and any methods
within determine the Objects Behaviour but so far no-one is able to tell me.
here be a typical Class header:
#interface Math : NSObject
#property (nonatomic, assign) int a;
#property (nonatomic, assign) int b;
#property (nonatomic, assign) int c;
-(int)mathemagic:(int)a adding:(int)b;
#end
and the corresponding Class implementation:
#implementation Math
#synthesize a = _a;
#synthesize b = _b;
#synthesize c = _c;
- (instancetype)init {
self = [super init];
if (self) {
_a = 0;
_b = 0;
_c = 0;
}
return self;
}
-(int)mathemagic:(int)a adding:(int)b {
_c = (a + b);
return _c;
}
#end
and finally in the appropriate places in my ViewController
#import "Math"
- (void)viewDidLoad {
[super viewDidLoad];
Math *theMath = [Math alloc]; // makes no difference if I init[]
theMath.a = 10;
theMath.b = 20;
NSLog (#" answer is %i",theMath.c);
// but still outputs to:
// answer is 0
}
Now I know can make an iVar and do it this way,
int d = [self.theMath mathemagic:theMath.a adding:theMath.b];
NSLog (#" sum: %i",d);
But i shouldn't have to. Stanford CS193P seems to always make the Class a property of the ViewController, but then everything is again expressed as self.theMath.whatever and the Data Model is no longer encapsulated away from the VC ? Maybe Stanford leaves advanced distractions to Java graduates till later.
Well for this person who's read David Flanagan's "Java in A Nutshell" ,
and Niemeyer-Knudsen's "Learning Java", It's later Now.
I shouldn't have to touch theMath.c, just by assigning values to [ theMath.a ] and [ theMath.b ] should be enough.
Where am I wrong?
I think that is because you are setting a and b = 0 in alloc init . and you are not calling [self mathemagic:a adding:b] anywhere.
I think im Math.m you should change -(instancetype)init to
- (instancetype)initWith:(int)a andb:(int)b {
self = [super init];
if (self) {
_c = [self mathemagic:a adding:b];
}
return self;
}
and in viewDidLoad use
Math *theMath = [[Math alloc]initWith:10 andb:20];
Hope this helps :)
I think you have a misconception of how Objective-C classes work.
First of all, it takes two steps to create an object in Objective-C. You must both:
Dynamically allocate memory for the new object
Initialize the newly allocated memory to appropriate values
So your Math instance initialization should look like this:
Math *theMath = [[Math alloc] init];
Just calling alloc zeroes out all instance variables of the object. Although in your case it makes no difference using [Math alloc] or [[Math alloc] init], it's not good programming style.
Second, if by "automatically display behaviour" you mean logging the result of mathemagic:adding: method, then you should pass it as an argument to NSLog function instead of theMath.c
NSLog(#" should show the sum being %i", [theMath mathemagic:theMath.a adding:theMath.b]);

How to properly code "init" method in Objective-C, prevent "Expression result unused" warning

I was given a main by my professor that I have to make the .h and .m files for in Objective-C using Xcode. I have it almost done but have two warnings that I can't figure out how to get rid of, "Expression result unused". The errors have to do with my init method for making an array of size 100. Here is where the main creates an object and then calls the method I have coded improperly, this part is given from my teacher and I can't change it:
// main.m
// ChutesAndLadders
#import <Foundation/Foundation.h>
#import "ChutesAndLadders.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
ChutesAndLadders *cl = [[ChutesAndLadders alloc]init];
[cl initBoard];
[cl makeChutes:10];
[cl makeLadders:10];
int chutes=0;
int ladders=0;
for(int i=0;i<cl.board.count;i++){
NSString * cell = (NSString *)[cl.board objectAtIndex:i];
int additionalSpaces = (int)[cl addToMove:cell];
if(additionalSpaces>0)
ladders++;
else if (additionalSpaces<0)
chutes++;
}
[cl printBoard];
}
return 0;
}
Here is relevant parts of my method:The error is from the "initBoard" method above, and "initWithCapacity" line below. I know that the two warnings are describing the same error. The code below is mine so I can change it, unlike above.
#synthesize board = _board;
-(id) initBoard
if((self = [super init]){
[board intiWithCapacity: 100];
for(int i = 0; i < 100; i++){
[board addObject:#""];
}
}
return self;
}
and here is the relevant code from my declaration of "board" in the .h file:
#interface ChutesAndLadders : NSObject{
#private
NSMutableArray * board;
}
#property (readwrite, retain) NSMutableArray *board;
-(id) initBoard;
I have tried a lot of small changes in syntax but to no avail. Please don't tell me the solution directly, but a nudge or a shove in the right direction I think is all I need and I will post the solution once I figure get it.
Pseudo solution:
#synthesize board=board;
-(void) initBoard{
_board = [board initWithCapacity: 100];
for(int i =0; i < 100; i++){
[_board addObject:#""];
}
}
This eliminated one of the warnings, but my program is not running properly so I still have work to do in other methods. This specific question as been addressed though and I have a better understanding of how this is working now.
Actual Solution
#synthesize board=_board;
-(void) initBoard{
_board = [NSMutableArray arrayWithCapacity:100];
for(int i =0; i < 100; i++){
[_board addObject:#" "];
}
}
This is the one that finally worked per Hermann. I swear that I tried this once and it didn't work, so I kept playing around and changing things(messing it up more) and then eventually got back to this and it of course worked. Xcode lies sometimes I think to irritate me.
Edit because at first I overlooked something:
Simply use:
ChutesAndLadders *cl = [[ChutesAndLadders alloc]initBoard];
because within initBoard you are calling [super init] anyway.
Does your professor try to trick you?
It is difficult to say without having a look at all of the code.
You are synthesizing a property board which has the iVar _board.
Meaning you can access it's getter or setter respectively by self.board or [self board] or [self setBoard: ...].
But within the initBoard method it is accessed differently:
[board intiWithCapacity: 100];
Despite the fact that we do not see where board is actually allocated and that the result of the initWithCapacity method is not used, I am asking myself what board actually is. It is not the property (that would be self.board etc) nor is it the iVar (that would be _board).
Apparently just board refers to the private iVar that you declared. But
#synthesize board = _board;
creates an iVar with the name _board of the same type (NSMutableArray*).
Therefore in your method, board is different from _board.
Althoug I am editing this for the 3rd time now, I am still not getting it. The .m file was given by the teacher and you have to create a matching .h file, right? In that case you should go for
#private
NSMutableArray * _board;
Well, that is done automatically anyway but writing it with "_" would avoid the creation of a similar named iVar and therefore avoid likely errors.
Background AFAIK: Theoretically or even practically init may create a different instance than the one that alloc created. Therefore you should always use the return value of init.
In general - independent from the strange objective-c notation around object initialization - when ever a method returns a value, then
[object method];
is syntactically correct, runs fine and everything but the return value is unused. And that is what your warning message is all about. Even if you assign it to a dummy object like
SomeClass *someObject = [object method];
you would get the warning that `someClass' is an unused variable.
Last edit:
You are nearly there. However, this is a shortcut for current versions of Obj-C, not for older ones:
.h file:
#interface ChutesAndLadders : NSObject
#property (readwrite, retain) NSMutableArray *board;
-(void) initBoard;
#end
.m file:
// omitting #synthesize forces the compiler to autosynthesize. In that case it is named "_board"
-(void) initBoard
_board = [NSMutableArray arrayWithCapacity: 100]; // this implies the alloc
for(int i = 0; i < 100; i++){
[_board addObject:#""];
}
}
You're returning something that you're not using. Also read up on writing proper constructors/initializing objects in Objective-C.
As Hermann said you should do
ChutesAndLadders *cl = [[ChutesAndLadders alloc]initBoard];
and then in your initBoard method
self.board = [NSMutableArray alloc] intiWithCapacity: 100];
You are not following standard Objective-C conventions, and that gets you trapped.
You have an instance variable board. Objective-C convention is that all instance variables should start with an underscore character.
You have a property board, and #synthesize board = _board. The synthesize creates an instance variable with an underscore character, named _board. Now you have two instance variables: board and _board. One of them, _board, is the instance variable backing the board property. The other, board, has nothing to do with board property at all.
And then somewhere you have a harmless looking but fatal statement board = whatever. Which assigns to that totally useless instance variable board. Not _board, but board. If you wrote _board then you would be following Objective-C conventions: Assignment to instance variable using highly visible name. or self.board which would be the property.
And [board initWithCapacity:100] doesn't do anything. First, because the board instance variable (totally unrelated to board property or _board instance variable) hasn't been allocated. It's nil. Sending initWithCapacity to nil does nothing. Sending addObject to nil does nothing either.

Property '' not found on object of type 'id'

I'm getting Property 'aVariable' not found on object of type id when trying to read or write aVariable to the array. Shouldn't it be known what class the object is that I added? Also noticed that it works to read the value with NSLog(#" %#",[[anArray objectAtIndex:0] aVariable]);
I'm a beginner at Objective C so it might be some simple thing I'm not getting.
AnObject
#interface AnObject : NSObject
#property (nonatomic,readwrite) int aVariable;
#end
AnotherObject
#interface AnotherObject : NSObject
#end
test.h
#import "test.h"
#implementation AnObject
#synthesize aVariable;
- (id)init
{
self = [super init];
if (self) {
aVariable=0;
}
return self;
}
#end
test.m
#implementation AnotherObject
- (id)init
{
self = [super init];
if (self) { }
return self;
}
- (NSMutableArray*) addToArray
{
NSMutableArray* anArray = [[NSMutableArray alloc] initWithCapacity:0];
AnObject* tempObject = [[AnObject alloc] init];
tempObject.aVariable=10;
[anArray addObject:tempObject];
// Property 'aVariable' not found on object of type 'id'
[anArray objectAtIndex:0].aVariable=[anArray objectAtIndex:0].aVariable + 1;
// Property 'aVariable' not found on object of type 'id'
NSLog(#" %i",[anArray objectAtIndex:0].aVariable);
// This works
NSLog(#" %i",[[anArray objectAtIndex:0] aVariable]);
return anArray;
}
#end
This code:
[anArray objectAtIndex:0].aVariable
Can be broken down into 2 sections:
[anArray objectAtIndex:0]
This returns an id- because you can put any type of object into an array. The compiler doesn't know what type is going to be returned by this method.
.aVariable
This is asking for the property aVariable on the object returned from the array - as stated above, the compiler has no idea what this object is - it certainly won't assume that it is an AnObject, just because that is what you added a line or two earlier. It has to evaluate each statement on its own. The compiler therefore gives you the error.
It is a little more forgiving when using accessor methods:
[[anArray objectAtIndex:0] aVariable];
This will give you a warning (that the object may not respond to the selector) but it will still let you run the code, and luckily enough your object does respond to that selector, so you don't get a crash. However this is not a safe thing to rely on. Compiler warnings are your friends.
If you want to use the dot notation, you need to tell the compiler what type of object is being returned from the array. This is called casting. You can either do this in two steps:
AnObject *returnedObject = [anArray objectAtIndex:0];
int value = returnedObject.aVariable;
Or with a mess of brackets:
int value = ((AnObject*)[anArray objectAtIndex:0]).aVariable;
The extra brackets are required to allow you to use dot notation when casting. If you want to use the accessor methods, you need fewer round brackets but more square brackets:
int value = [(AnObject*)[anArray objectAtIndex:0] aVariable];
-[NSArray objectAtIndex:] returns an id pointer. Since id does not contain information about your protocol the compiler cannot know the object has this property you declared; that is why it complains.
You can solve this by either cast the return value of objectAtIndex: or by using the getter/setter notation, i.e. [anArray objectAtIndex:0] setAVariable:...]. Also make sure you import your protocol definition, otherwise the compiler might also not know about the declared method and issue a warning.

%d doesn't show integer properly

As I try to write
NSLog(#"%d", myObject.myId);
where myId is int, console gives some hight number like 70614496. And when I use #"%#", I get exception -[CFNumber respondsToSelector:]: message sent to deallocated instance 0x466c910.
Why is it so?
Here's definition of myObject:
#interface myObject : NSObject {
int myId;
NSString *title;
}
#property(nonatomic) int myId;
#property(nonatomic, retain) NSString *title;
#end
#implementation myObject
#synthesize myId, title;
- (void)dealloc {
[title release];
[super dealloc];
}
#end
Carl's doing a good job of trying to get your code to compile. Just another word on the %# format specifier, since your question seems to imply some confusion.
When you use %# in an NSLog format, you're actually sending the argument a description message, and printing the resulting string. If myId is an int and not an instance of NSObject, it doesn't respond to the description message. Use %d for int values and %# for NSNumber instances.
Here's an example showing that it should work the way you first tried it, if your class/object/instance variable are all set up correctly:
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
{
int myId;
}
#property int myId;
#end
#implementation MyClass
#synthesize myId;
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyClass *myObject = [[MyClass alloc] init];
myObject.myId = 5;
NSLog(#"%d", myObject.myId);
[myObject release];
[pool drain];
return 0;
}
Your code shows you doing the equivalent of this line:
NSLog(#"%d", MyClass.myId);
Which doesn't even compile, giving the error:
foundation.m:21: error: accessing unknown 'myId' class method
Given that second error message, I think the %d is working fine, but somewhere else you’re assigning a CFNumber or NSNumber to the myID property when you should be assigning an integer.
It looks like your instance of myObject was autoreleased at some time and is no longer actually valid. That is why you see the strange NSNumber issue when you try to dereference it as an object. When you just ask for an integer, you won't get any kind of error, it will just display the pointer's address coerced into an integer.
To see when your myObject gets dealloced, try adding something like this to the dealloc method in your class:
- (void)dealloc
{
NSLog(#"dealloc called on %#", self);
[title release];
[super dealloc];
}
I bet you will find this logging a dealloc before you see the log for your myObject.myID.
I would try simply changing this:
#property(nonatomic) int myId;
to this:
#property(nonatomic, assign) int myId;
And let me know your result, I suspect that obj-c is doing something funky when assigning an old int to the new int?
Check out the String Format Specifiers to see how to format NSLog statements. It's easy to get lazy with NSLog because objects have a built-in -description method that returns a formatted string. For scaler values, you have to use the proper formatter.
Because precision changes as you move from hardware to hardware, it's better to get in the habit of using object conversions to log values. In this case:
NSLog(#"%#", [[NSNumber numberFromInt:myObject.myId] stringValue]);
This will print correctly always.
Edit#1: I apologize. I was sleep deprived when I wrote the above. What I actually intended was to warn against using a simple int versus NSInteger as well as printing with NSNumber numberWithInteger:.
Consider the following run on 64-bit hardware.
int x=pow(2,63);
NSLog(#"x=%d",x); //prints x=2147483647
NSInteger n=pow(2,63);
NSLog(#"n=%d",n); // prints n=-1
NSLog(#"n=%#",[[NSNumber numberWithInteger:n] stringValue]); // prints n=9223372036854775807
In the old days of 8-bit systems, you ran into problems with problems with using 8-bit 'int' all the time. Running a for-loop with more than 256 iterations required a long. With a 32-bit int you won't see those kinds of issues and will never develop the habit of tracking the size of your intvariables.
This can lead to pernicious bugs that are nearly impossible to track down because they only occur with very specific and rare values in the data.
Writing for the iPhone (or other future mobiles/platforms) means writing on potentially highly variable hardware just like we had to do in the old days. It's best to acquire the habit early of using system and API specific definitions.
Edit#2:
where myId is int, console gives some
hight number like 70614496.
(1) If it prints a different number each time you run, then you're probably assigning a pointer to the int when you set it. The NSLog is correctly printing the value of the pointer as an int.
(2) if it prints the same number each time, the you probably have an overflow issue like in my first edit above.
In either case, you need to look at code where you assign the value to the id property and not where you print it.

In Objective-C, can I declare #property on a c-array of floats?

thing.h
#interface Thing : NSObject
{
float stuff[30];
}
#property float stuff;
#end
thing.m
#implementation Thing
#synthesize stuff;
#end
I get error: type of property 'stuff' does not match type of ivar 'stuff'
I don't want to use an NSArray because I'd have to make the floats into NSNumbers (right?) and that's a pain to do math with.
Update: I've noticed similar answers had guesses and trial answers. While I appreciate the attempts by non-Objective-C folks, I'm hoping for a definitive answer whether it's possible or not.
OK, I have compiled up the following code at it works as expected.
FloatHolder.h
#interface FloatHolder : NSObject {
int _count;
float* _values;
}
- (id) initWithCount:(int)count;
// possibly look into this for making access shorter
// http://vgable.com/blog/2009/05/15/concise-nsdictionary-and-nsarray-lookup/
- (float)getValueAtIndex:(int)index;
- (void)setValue:(float)value atIndex:(int)index;
#property(readonly) int count;
#property(readonly) float* values; // allows direct unsafe access to the values
#end
FloatHolder.m
#import "FloatHolder.h"
#implementation FloatHolder
#synthesize count = _count;
#synthesize values = _values;
- (id) initWithCount:(int)count {
self = [super init];
if (self != nil) {
_count = count;
_values = malloc(sizeof(float)*count);
}
return self;
}
- (void) dealloc
{
free(_values);
[super dealloc];
}
- (float)getValueAtIndex:(int)index {
if(index<0 || index>=_count) {
#throw [NSException exceptionWithName: #"Exception" reason: #"Index out of bounds" userInfo: nil];
}
return _values[index];
}
- (void)setValue:(float)value atIndex:(int)index {
if(index<0 || index>=_count) {
#throw [NSException exceptionWithName: #"Exception" reason: #"Index out of bounds" userInfo: nil];
}
_values[index] = value;
}
#end
then in your other application code you can do something like the following:
** FloatTestCode.h **
#import <Cocoa/Cocoa.h>
#import "FloatHolder.h"
#interface FloatTestCode : NSObject {
FloatHolder* holder;
}
- (void) doIt:(id)sender;
#end
** FloatTestCode.m **
#import "FloatTestCode.h"
#implementation FloatTestCode
- (id) init
{
self = [super init];
if (self != nil) {
holder = [[[FloatHolder alloc] initWithCount: 10] retain];
}
return self;
}
- (void) dealloc
{
[holder release];
[super dealloc];
}
- (void) doIt:(id)sender {
holder.values[1] = 10;
}
The type of the property must match the type of the instance variable it will be stored in, so you could do something like
#interface Thing : NSObject
{
float stuff[30];
}
#property float[30] stuff;
#end
and it should work. I wouldn't recommend it though.
I'm guessing you're looking for something like indexed properties from Delphi. The closest you'll get is something like the following.
#interface Thing : NSObject
{
float stuff[30];
}
- (void) setStuff:(float)value atIndex:(int)index;
- (float) getStuffAtIndex:(int)index;
#end
You can't do it the way you want to do it. You can jump through some hoops and get something similar, e.g. using Daniel's solution, but it's not quite the same thing. The reason you can't do it is that arrays are not lvalues in C. An lvalue is something that can appear on the left-hand side of an assignment. The following code is invalid C:
float stuff1[30], stuff2[30];
stuff1 = stuff2; // ERROR: arrays are not lvalues
As a consequence, you can't declare properties whose types are not lvalues.
Daniel's FloatHolder answer has a major bug (edit: he's now fixed it). It only allocates memory for one float and not for the whole array.
The line:
_values = malloc(sizeof(float));
Should be:
_values = malloc(sizeof(float) * count);
Otherwise it seems to be a good answer. Sorry couldn't work out how to reply directly. (edit: I didn't have the necessary privilege on stackoverflow then.)
Even if you could get that to compile, it wouldn't behave well. 'stuff' would return a float*, and the client would have no idea how long the array way; 'setStuff:' would just change the pointer, and you'd either be pointing to stack-allocated data that would vanish out from under you or heap-allocated data that would leak because it wouldn't know to free it.
I'm not well-versed in Objective-C 2.0, but I'm guessing that the issue might be caused by the fact that a C array is essentially just a pointer to the first element of the array, meaning that the type of float stuff[30] is actually float *, not merely a float.