PDA

View Full Version : question about pointers in objective-C




Glama
Feb 8, 2009, 10:33 AM
Im toiling through the book "Learn Objective-C on the Mac". Pretty good book, but the code in chapter 5 looks different than the code in chapter 3, and Im hoping someone can help me understand.

In one instance, a main function looks like this. (Ive trimmed out some repetitive code for the other elements of the array so there is less code to look at.)

int main (int argc, const char * argv[])
{
id shapes[4];
shapes[0] = [Circle new];
drawShapes (shapes, 4);
return (0);
}


Later, the book has a main function that looks like this:

int main (int argc, const char * argv[])
{
Car *car;

car = [Car new];
[car print];

return (0);
}

My question is about the bolded lines. Specifically, why did he need a pointer in the second example ( Car *car; ) but not the first ( id shapes[4]; ) ??



ayasin
Feb 8, 2009, 10:43 AM
id is a typedef for a pointer to a struct (objc_class*). In both cases he's using a pointer. Sometimes people use typedefs to avoid having to constanty put *. You'll see this alot in CF objects like CFStringRef is really CFString*.

Glama
Feb 8, 2009, 10:49 AM
I mostly understand what you said. Im still learning. Thanks for the help. :)

Why didnt he have to write

id *shapes[4];

Or something like that?

lee1210
Feb 8, 2009, 11:44 AM
In /Developer/SDKs/MacOSX10.4u.sdk/usr/include/objc/objc.h on my system the following lines appear:

typedef struct objc_object {
Class isa;
} *id;

so when you see id, it can be replaced with 'objc_object *'. So if you do this substitution, the first example looks like:
int main (int argc, const char * argv[])
{
objc_object *shapes[4];
shapes[0] = [Circle new];
drawShapes (shapes, 4);
return (0);
}

So shapes is a list of pointers after all. It just turns out that it's a pointer to the most generic possible object in the language.

id *shapes[4];
would turn into:
objc_object **shapes[4];
Which would be an array of 4 pointers to pointers to objc_objects, which is not what you want.

I'm not familiar with the book, but i can't say i blame the author for not going into this sort of details. It would hope that there was at least a mention somewhere that id is a "generic" object pointer, but beyond that I wouldn't expect them to show you how id is actually defined, etc.

-Lee

Glama
Feb 8, 2009, 11:51 AM
This stuff is so different from anything Ive done before.

lee1210
Feb 8, 2009, 11:56 AM
This stuff is so different from anything Ive done before.

Is this your first foray into programming, or into Object-Oriented programming? If not, what languages have you worked in before? Does this book assume a knowledge of C that you didn't have before starting it? These things might be causing some of the confusion. There are other books on learning objective-c that don't assume or require a knowledge of C that might be a better fit if you do not have such a background.

-Lee

EDIT: To expand slightly on what was written above, Class is defined as:
typedef struct objc_class *Class;
and objc_class is defined in objc_class.h as:

struct objc_class {
struct objc_class *isa;
struct objc_class *super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;

struct objc_method_list **methodLists;

struct objc_cache *cache;
struct objc_protocol_list *protocols;
};


so an id contains a single variable that is a structure, that has all of the "building blocks" for an objective-C class.

ayasin
Feb 8, 2009, 12:16 PM
EDIT: To expand slightly on what was written above, Class is defined as:
typedef struct objc_class *Class;
and objc_class is defined in objc_class.h as:

struct objc_class {
struct objc_class *isa;
struct objc_class *super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;

struct objc_method_list **methodLists;

struct objc_cache *cache;
struct objc_protocol_list *protocols;
};


so an id contains a single variable that is a structure, that has all of the "building blocks" for an objective-C class.

I wouldn't worry about any of this right now. Get the basics down then you can dive into deeper layers. If you get to deep right away you'll only end up feeling overwhelmed and turned off.

ayasin
Feb 8, 2009, 12:20 PM
I mostly understand what you said. Im still learning. Thanks for the help. :)

Why didnt he have to write

id *shapes[4];

Or something like that?

The really short answer to this is that when you compile a program in c/obj-c/c++ the first thing that happens is that the pre-processor runs. One if it's functions is effectively search and replace much like a word processor. When it comes across id it's going to replace it with "struct objc_class*", as pointed out above if you put id* then it would become "struct objc_class* *" which means something else. It's a little confusing at first, but hang in there, it'll get easier :).

bpwats
Feb 8, 2009, 01:12 PM
My suggestion would be, to first work through "Learn C on the Mac" in the same series.

Glama
Feb 8, 2009, 02:03 PM
Thanks everybody.

Ive done Visual Basic, but not any kind of C. I thought about getting "Learn C on the Mac", but it looked too easy, since it was aimed at people who had never programmed before.

gnasher729
Feb 8, 2009, 04:21 PM
My question is about the bolded lines. Specifically, why did he need a pointer in the second example ( Car *car; ) but not the first ( id shapes[4]; ) ??

Basically, "id" means "AnyObjectiveCObject *".

Howiieque
Feb 8, 2009, 08:41 PM
id is to declare a pointer, which can refer to any objective-c object. Class * is to declare a certain pointer to an object belong to that class. in object-c a object variable is a pointer to a structure. many books recommended you to use the static one, like NSString *, NSArray. for two reason. one is to fully understand thing. the second is let the compiler to check the error for you.

Glama
Feb 8, 2009, 10:46 PM
Awesome. Ive added this great info to my notes. Thanks again everyone.

slb
Feb 9, 2009, 11:18 PM
"id" lets you do dynamic typing, which will defer type checking until runtime. One example of why you would do this is NSArray, which can hold objects of any type in its arrays. More on this here (http://www.macdevcenter.com/pub/a/mac/2003/04/28/objective-c.html).

You should also know that people rarely use the +new method to create an object (I've actually never seen it used). You should almost always use a combination of +alloc and -init. It's explained fully here (http://burks.bton.ac.uk/burks/language/objc/iaq/q3.htm).

Sander
Feb 10, 2009, 06:35 AM
The really short answer to this is that when you compile a program in c/obj-c/c++ the first thing that happens is that the pre-processor runs. One if it's functions is effectively search and replace much like a word processor. When it comes across id it's going to replace it with "struct objc_class*", as pointed out above if you put id* then it would become "struct objc_class* *" which means something else. It's a little confusing at first, but hang in there, it'll get easier :).

Not quite: id is a typedef and not a #define. It's not touched by the preprocessor.

mdeh
Feb 10, 2009, 11:25 AM
Is this your first foray into programming, or into Object-Oriented programming? If not, what languages have you worked in before? Does this book assume a knowledge of C that you didn't have before starting it? These things might be causing some of the confusion. There are other books on learning objective-c that don't assume or require a knowledge of C that might be a better fit if you do not have such a background.

-Lee

EDIT: To expand slightly on what was written above, Class is defined as:
typedef struct objc_class *Class;
and objc_class is defined in objc_class.h as:

struct objc_class {
struct objc_class *isa;
struct objc_class *super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;

struct objc_method_list **methodLists;

struct objc_cache *cache;
struct objc_protocol_list *protocols;
};


so an id contains a single variable that is a structure, that has all of the "building blocks" for an objective-C class.



May I just jump in a clarify some terminology.


for instance:

int i /* i is a variable of type int */
int *i /* i is a pointer to integer */
typedef int * iptr /* here is the question. */

I assume that iptr is now a ptr to integer, which ???implies that the modifier "*" is grouped with the int type?

I ask this as it is always a little confusing when you get, this.

typedef struct mystruct { } * ptrS /* ptrS is a pointer to struct???

So the "*" again "belongs" to the struct???

Anyone care to clear up the confusion?

ChrisA
Feb 10, 2009, 12:28 PM
I mostly understand what you said. Im still learning. Thanks for the help. :)

Why didnt he have to write

id *shapes[4];

Or something like that?


I think what you missed is that in C arrays ARE pointers. If I write

int foo[4];

Then "foo" is a pointer to a block of memory large enough to hold four integers. The expression foo[0] means the integer pointer to by foo and foo[1] means the next one.

int foo[4];

is nice C shorthand for

{ int *foo;
foo = calloc(4, sizeof(int));
.... some stuff...
free(foo);}

Note that "some stuff" could include "foo[3]" because you can always put brackets after pointers no matter how you declared the pointer

Objective C and C++ also have some other nice shorthand notations. But that all just do the above.

kalimba
Feb 10, 2009, 01:07 PM
May I just jump in a clarify some terminology.


for instance:

int i /* i is a variable of type int */
int *i /* i is a pointer to integer */
typedef int * iptr /* here is the question. */

I assume that iptr is now a ptr to integer, which ???implies that the modifier "*" is grouped with the int type?

I ask this as it is always a little confusing when you get, this.

typedef struct mystruct { } * ptrS /* ptrS is a pointer to struct???

So the "*" again "belongs" to the struct???

Anyone care to clear up the confusion?

Here's how I look at it. A typedef is simply a mechanism by which you take one "thing" and give it a new, unique name (or alias, if you like). The syntax is:
typedef <a bunch of stuff you want to alias> your_new_identifier_name;

It some cases, a typedef makes perfect sense when you see it, as in this example:
typedef unsigned int uint;

Once you start adding struct or enum definitions and pointers to the "stuff you want to alias" part, things can start to get confusing, as in the following examples:
typedef enum { yes, no, maybe } Response;
typedef struct { float x; float y; float z; } *PointPointer;

Remembering the syntax of the typedef keyword, you know that the "your_new_identifier_name" part is an identifier, so it must conform to the naming rules for identifiers. Therefore, it can not ever begin with a "*" or "}" character. In my PointPointer example above, it appears that I'm defining a new type of "*PointPointer", but my new type is actually "PointPointer". The "*" can not belong to the identifier name and must belong in the "stuff I want to alias" part.

So now that you know your typedef can only assign an alias to an identifier, how do you decipher what you're aliasing? For this, I prefer to read the typedef from right-to-left. Using the PointPointer typedef above and reading backwards, I describe it like this:

Parse "PointPointer". I know the type identifier. "PointPointer is a type..."
Parse "*". I know the type points to something. "PointPointer is a type that points to..."
Parse braces. When I hit a brace, I know that braces qualify something bigger, so I "push" the contents onto an imaginary stack and proceed.
Parse "struct". I know what the type points to. "PointPointer is a type that points to a struct..."
Now that I know what the braces qualify, I can pop them from my stack. "PointPointer is a type that points to a struct of three float coordinates."
Parse "typedef". I'm done.


Hope this helps.

Catfish_Man
Feb 10, 2009, 03:54 PM
I think what you missed is that in C arrays ARE pointers. If I write

int foo[4];

Then "foo" is a pointer to a block of memory large enough to hold four integers. The expression foo[0] means the integer pointer to by foo and foo[1] means the next one.

int foo[4];

is nice C shorthand for

{ int *foo;
foo = calloc(4, sizeof(int));
.... some stuff...
free(foo);}

Note that "some stuff" could include "foo[3]" because you can always put brackets after pointers no matter how you declared the pointer

Objective C and C++ also have some other nice shorthand notations. But that all just do the above.

It's a little fuzzier than that. Good chance it's alloca not calloc, for example, in which case the compiler probably avoids making a pointer and just refers to stack offsets. That's an implementation detail though.

mdeh
Feb 10, 2009, 05:57 PM
Here's how I look at it. A typedef is simply a mechanism by which you take one "thing" and give it a new, unique name (or alias, if you like). The syntax is:
typedef <a bunch of stuff you want to alias> your_new_identifier_name;

It some cases, a typedef makes perfect sense when you see it, as in this example:
typedef unsigned int uint;

Once you start adding struct or enum definitions and pointers to the "stuff you want to alias" part, things can start to get confusing, as in the following examples:
typedef enum { yes, no, maybe } Response;
typedef struct { float x; float y; float z; } *PointPointer;

Remembering the syntax of the typedef keyword, you know that the "your_new_identifier_name" part is an identifier, so it must conform to the naming rules for identifiers. Therefore, it can not ever begin with a "*" or "}" character. In my PointPointer example above, it appears that I'm defining a new type of "*PointPointer", but my new type is actually "PointPointer". The "*" can not belong to the identifier name and must belong in the "stuff I want to alias" part.

So now that you know your typedef can only assign an alias to an identifier, how do you decipher what you're aliasing? For this, I prefer to read the typedef from right-to-left. Using the PointPointer typedef above and reading backwards, I describe it like this:

Parse "PointPointer". I know the type identifier. "PointPointer is a type..."
Parse "*". I know the type points to something. "PointPointer is a type that points to..."
Parse braces. When I hit a brace, I know that braces qualify something bigger, so I "push" the contents onto an imaginary stack and proceed.
Parse "struct". I know what the type points to. "PointPointer is a type that points to a struct..."
Now that I know what the braces qualify, I can pop them from my stack. "PointPointer is a type that points to a struct of three float coordinates."
Parse "typedef". I'm done.


Hope this helps.


Thanks for that detailed reply. It clears the fog! :)