PDA

View Full Version : Objective-C tutorial?


computergeek6
2008.08.07, 02:35 AM
I'm trying to figure out Objective-C and Cocoa, but I'm getting confused. Is there a good tutorial for migrating from C to Objective-C and Cocoa? If not, please recommend a learning path.

AnotherJake
2008.08.07, 03:05 AM
I was gonna say search the forums, but even Google gave me junk for some reason. I guess I didn't figure out the magic werdz this time...

You'll want to purchase a copy of: Cocoa Programming for Mac OS X (http://www.amazon.com/Cocoa-Programming-Mac-OS-3rd/dp/0321503619/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1218088821&sr=8-1) by Aaron Hillegass.

You will also want to read Apple's docs on getting started with objective-c and Cocoa.

There are a bunch of other places which I'm sure others will be quick to recommend, but those are the top two.

Man With No Name
2008.08.07, 10:06 AM
If you want to learn about the Objective-C language itself, I highly suggest Programming in Objective-C (http://www.amazon.com/Programming-Objective-C-2-0-Developers-Library/dp/0321566157/ref=pd_bbs_sr_2?ie=UTF8&s=books&qid=1218113754&sr=1-2).

You might take a look over at CocoaLab (http://www.cocoalab.com/index.php) and their BecomeAnXcoder online book, I found that to be helpful.

And last but not least, check out CocoaDev (http://cocoadev.com/), it's a very comprehensive wiki for Cocoa. If I can't figure something out from the documentation, that site is the first thing I check for information.

Hope that helps!

computergeek6
2008.08.08, 12:14 AM
Thank you!

Willem
2008.08.09, 06:56 AM
You'll want to purchase a copy of: Cocoa Programming for Mac OS X by Aaron Hillegass.
I'll second this. That book is excellent and it made everything very clear to me coming from a C++/MFC background.

Hairball183
2008.08.09, 11:59 AM
(Aside: are you the Willem from C3D forums?)

Fenris
2008.08.09, 01:02 PM
Thirding Hillegass' book. Make sure you actually buy it, it's one of the cornerstones of the community's adoption of Cocoa, and I heard something about it not selling well due to PDF piracy. :/

Hairball183
2008.08.09, 01:11 PM
Its a REALLY good book.

Willem
2008.08.09, 02:08 PM
(Aside: are you the Willem from C3D forums?)
Yep, same guy.

computergeek6
2008.08.09, 06:31 PM
How would I get the value of an NSTextField in Interface Builder from the Obj-C code?

AnotherJake
2008.08.09, 06:35 PM
How would I get the value of an NSTextField in Interface Builder from the Obj-C code?

The interface (what you make in InterfaceBuilder) doesn't "get" a value from your Obj-C code, it gets "set" by your Obj-C code.

computergeek6
2008.08.09, 06:43 PM
I know, but I can't figure out how to store the text from my NSTextField into an NSString. I have a button and a text-field in a window, the user has to put something in the field, then click the button, then the button sends an event to the instance of my class, then the class needs to get the text in the text field, and set the caption of the button to whatever is in the text field.

AnotherJake
2008.08.09, 06:52 PM
Oh, I misunderstood your question! Sorry bout that... You can do:

NSString *theStringFromMyTextField = [myTextField stringValue];
[myButton setTitle:theStringFromMyTextField];

computergeek6
2008.08.09, 06:53 PM
How do I set the name of the text field? In Interface Builder, or in code?

AnotherJake
2008.08.09, 07:01 PM
Not exactly sure what you mean by "name". If you mean the text field's contents, you can do it from either IB or code:

[myTextField setStringValue:@"my title"];

or

[myTextField setStringValue:myString];

computergeek6
2008.08.09, 07:02 PM
I mean what I do to let the compiler know that "myTextBox" is a reference to the textbox in my window.

AnotherJake
2008.08.09, 07:11 PM
I think I might understand what you mean now (maybe). In your header you'll set the name of your text field in your interface declaration using IBOutlet, like so:

@interface MyClass : NSObject
{
IBOutlet NSTextField *myTextBox;
}

@end

Then IB would know there is an outlet named myTextBox in your class (which must be instantiated somehow in IB) which you can then hook up to your textField. When you control drag from your instantiated object from that class in IB to your textField, a little floating window will pop up with myTextBox listed as an outlet you can use.

computergeek6
2008.08.09, 07:16 PM
It worked!!!! THANK YOU!

AnotherJake
2008.08.09, 07:19 PM
Hehe. We aim to please. ;)

computergeek6
2008.08.09, 07:58 PM
How do I get my class to receive delegate events?

AnotherJake
2008.08.09, 08:13 PM
It depends, but usually:

[someOtherClass setDelegate:self];

Which implies that you need to have access to an instance of the class you wish to receive delegate messages from.

You might need to also declare that your class supports the other class's delegate protocol. Assuming you know the name of the delegate protocol, you can do that something like so in your header:

@interface MyClass : NSObject <SomeOtherClassDelegate> {
}
@end

An alternative to receiving delegate messages is that you can often register for equivalent notifications from the class you're interested in. For notifications you won't have to have access to an instance of the class. I generally prefer notifications, but sometimes delegate is the only way, depending on the situation.

computergeek6
2008.08.09, 10:16 PM
So, if I do that, then any function I put in my .m file that has the same name/type/parameters as a delegate function gets called when event x happens?

AnotherJake
2008.08.09, 10:39 PM
It's technically referred to as a "method" not a "function", but yes, that's the general idea.

Some classes may assume that you implement *all* delegate methods declared in their delegate protocol, and some classes may check beforehand whether or not your class responds to them. I don't have time to lay the concept out explicitly here. You should definitely study up on delegates and protocols (informal and formal). Delegation and notification are hugely important (and infinitely handy) features to understand.

computergeek6
2008.08.09, 11:29 PM
Ok! I'll study about delegates and keep experimenting. Thanks for all the help! I've been putting off learning Cocoa and Obj-C for long enough.

computergeek6
2008.08.10, 02:04 AM
Why doesn't this work?

- (NSNumber)operate {
float an, bn;
float res;
an = [op floatValue];
bn = [working floatValue];
if(oprt == 0) {
res = an / bn;
} else if(oprt == 1) {
res = an * bn;
} else if(oprt == 2) {
res = an + bn;
} else if(oprt == 3) {
res = an - bn;
}
return [NSNumber numberWithFloat: res];
}

It says "error: can not use an object as parameter to a method" and
"error: incompatible types in return"
What is wrong?

AnotherJake
2008.08.10, 02:15 AM
Try:

- (NSNumber *)operate

instead. That is the first (perhaps only) major issue I see. It seems to me that it should probably work other than that. Instances are (represented by) pointers, and so require the * operator to label them as such. Remember that you are returning an instance of an NSNumber. Basically you will always (well, often) have to include the * next to your return type when dealing with Obj-C.

computergeek6
2008.08.10, 02:27 AM
Thank you! I've been grappling with that error for about 1.5 hours.

computergeek6
2008.08.10, 02:42 AM
What causes "warning: assignment from distinct Objective-C type"?
Edit: Never mind.

computergeek6
2008.08.10, 03:04 AM
This code:
- (NSNumber *)operate {
float an, bn;
float res;
an = [op floatValue];
bn = [working floatValue];
if(oprt == 0) {
res = an / bn;
} else if(oprt == 1) {
res = an * bn;
} else if(oprt == 2) {
res = an + bn;
} else if(oprt == 3) {
res = an - bn;
}
return [NSNumber numberWithFloat: res];
}
Receives signal EXC_BAD_ACCESS and crashes. Does anyone know what's wrong with it? I think it's the an = [op floatValue]; line, but I'm not sure.

AnotherJake
2008.08.10, 03:18 AM
It is entirely possible you forgot to retain op in some other section of code when it was created, so when you try to call it to get a float value from it, it ain't there. You can try checking to see if it's nil right before an = [op floatValue];

if (op == nil)
{
NSLog(@"Apparently op doesn't exist, which likely means it wasn't retained properly.");
exit(0);
}
else
an = [op floatValue];

Incorrect retains are probably the root of most of the initial bugs I encounter with my own code. They're easy to forget about when you're pounding out code.

computergeek6
2008.08.10, 03:25 AM
I added debug lines, and op isn't nil before that statement is run. Could there be a problem if the string doesn't have a decimal point?

AnotherJake
2008.08.10, 03:35 AM
Doubt it, but anything is possible. You'll have to start breaking it down. Replace op's return value with an actual float, like:

an = 123.432f;

If it still goes bad access then op is off the hook (presumably). Do the same for the other instance variables you're sending messages to. Once you find out for sure which message is causing the fit then you can start backtracking through your code on it.

computergeek6
2008.08.10, 03:42 AM
I think it's op, because when I replace op with 3.14159265, it doesn't crash.
Lines that I think might be the problem:
op = [op initWithString: @""];
op = working;

AnotherJake
2008.08.10, 03:54 AM
I should caution that just because an instance doesn't come up nil doesn't mean it isn't garbage. Seeing that it is nil just definitely confirms that it's garbage. No real good way to tell otherwise. Like I mentioned, an instance is represented as a pointer, and that pointer could be changed to anything if you aren't careful.

It's getting hard to say exactly what's up without seeing the rest of your code. What is "working"? Is "working" retained?

When you do:

op = [op initWithString: @""];

Remember that op isn't retained because it is being created with a convenience method which autoreleases it... i.e. it isn't being created with op = [[NSString alloc] init], which is automatically retained. (anything [[alloc] init] is automatically retained, otherwise it is usually considered to have been autoreleased upon creation)

computergeek6
2008.08.10, 04:04 AM
"working" is the string being edited currently, and "op" is the stored string.
I'm trying to make a calculator. "working" is retained, and I fixed it so that "op" is retained, as far as I know. Should I post the full source-code?

AnotherJake
2008.08.10, 04:12 AM
Should I post the full source-code?

Yes, that would be helpful. If it's too large to reasonably post here (or you're not comfortable posting it here for whatever reason), you can email it to me at anotherjake +AT+ mac.com instead.

computergeek6
2008.08.10, 04:25 AM
Full .m file: http://www.mediafire.com/?brcgcdgxlzg
It's not really commented, because I usually do that once the code is done.

AnotherJake
2008.08.10, 04:54 AM
Okay, got the file. Lots of retain problems. Definitely need to study up on the retain/release thing a bit more. For example, when you do something like this:

working = [working stringByAppendingString: @"0"];

You *must* retain it because the new "working" is being created by what is called a convenience method (i.e. not alloc inited). You should be doing something like this instead:

- (IBAction)zero:(id)sender {
NSString *newWorking = [[working stringByAppendingString: @"0"] retain];
[working release];
working = newWorking;
[self updateDisplay];
}

computergeek6
2008.08.10, 05:04 AM
why do you need the extra NSString?

AnotherJake
2008.08.10, 05:09 AM
Well you don't *have* to have it I suppose, but it's a standard pattern in Obj-c programming to create a new separate object, release the old one, and then set it to being the new one.

However you choose to do it, a new string is created by a convenience routine, and must be retained, and the old one must be released.

[edit] you could do it:

- (IBAction)zero:(id)sender {
[working release];
working = [[working stringByAppendingString: @"0"] retain];
[self updateDisplay];
}

But notice that the old one is released and the new one retained. That's the important part. Yes, you are creating a new string. That's the way it works.

computergeek6
2008.08.10, 05:18 AM
Thanks. Also, how do you know which routines are convenience routines?

AnotherJake
2008.08.10, 05:38 AM
If an object is created with an alloc-init, like:

SomeClass *myObject = [[SomeClass alloc] init];

Then it is retained for sure. If it wasn't created by an alloc init, then it is likely autoreleased, although there are exceptions from time to time, and documentation should point that out. With Apple's stuff it is almost always autoreleased if you didn't explicitly create it with an alloc init. Again, there are exceptions to how you should handle retains and releases for particular objects, like NSTimer should only be released with an invalidate message... Gotta check the docs.

With third-party code, you never know exactly what you get unless you read their docs or read their source. For instance, I am notoriously bad about returning retained objects in my custom inits rather than autoreleased objects, as I should. Generally though, again, you should expect that if you didn't create it with an alloc init, it is likely autoreleased.

It takes some practice and experience to understand the retain/release thing, but it is crucial to understand it. The number one thing to keep in mind is that it is going to be the source of the vast majority of your bugs for a while. If you get a mystery bug, assume it's a retain/release problem first thing!

Check your retains by sending objects the retainCount message if you are in doubt. Unfortunately retainCount doesn't tell you if a retain is autoreleased, but at least it can give you some indication of what's up.

[edit] bedtime approaches...

computergeek6
2008.08.10, 05:44 AM
I'm going to buy a tutorial e-book soon, so hopefully I'll pick up something from that. I'll post the source again when I think I've gotten the retain problems sorted out.

PS: Thanks for being so helpful, even though I'm just a noob.

Willem
2008.08.10, 06:47 AM
I'll throw in a suggestion that maybe, if you're just getting started, you might want to turn on garbage collection in your application. It's a build setting which is off by default. When that is on, you don't have to concern yourself with retain/release and code just becomes much easier to write.

IMO, anyway. I know there are many opinions on both sides of the garbage collection issue. :)

Willem
2008.08.10, 07:21 AM
Hey computergeek,

If you'd like, send me an email to willem at wantonhubris dot com with your mailing address and I'll send you my second edition of Hillegass's book. I bought the third edition so this second edition is just gathering dust at this point.

Some of the interface builder stuff may be out of date but it's still a great introduction to how everything works in Objective-C/Cocoa.

ThemsAllTook
2008.08.10, 11:34 AM
When you do:

op = [op initWithString: @""];

Remember that op isn't retained because it is being created with a convenience method which autoreleases it... i.e. it isn't being created with op = [[NSString alloc] init], which is automatically retained. (anything [[alloc] init] is automatically retained, otherwise it is usually considered to have been autoreleased upon creation)

For instance, I am notoriously bad about returning retained objects in my custom inits rather than autoreleased objects, as I should.

I think there might be a misconception here. Cocoa's object ownership policy is spelled out here: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Concepts/ObjectOwnership.html#//apple_ref/doc/uid/20000043-BEHDEDDB

You own any object you create.
You "create" an object using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy).

If you own an object, you are responsible for relinquishing ownership when you have finished with it.
One way to relinquish ownership of an object is to send it a release message. In Cocoa terminology, relinquishing ownership of an object is typically referred to as "releasing" an object.

If you do not own an object, you must not release it.

So, the "init*" methods don't directly have anything to do with memory management. It's just how you allocate/retrieve the object.

AnotherJake
2008.08.10, 11:58 AM
Hmm... I don't see where the misconception is, but I will look carefully over the rules again and see where I am off kilter. If you receive a new object which has been autoreleased by the creator then you'd better retain it or it will be gone at the next autopool release.

AnotherJake
2008.08.10, 12:14 PM
Oh, I think I see what you're getting at. Right, sorry, init has nothing to do with -- it's just alloc [or new or copy]. For some reason, when I'm thinking about it, I think in terms of the pattern: alloc (init) all the time -- goofy me.

[edit] Also, it was late last night, and the initWithString example is not what I meant to say. I meant to say op = [op stringByAppendingString: @"blah"];, which is actually what his code is doing. stringByAppendingString is creating a new autoreleased string which must be retained or it will be gone later.

[edit 2] Also, in his code, doing op = [[op stringByAppendingString:@"blah"] retain]; will orphan the original op. At the very least, it should be op = [[[op autorelease] stringByAppendingString:@"blah"] retain];

[edit 3] Sheesh! Yeah, another edit... Also, I didn't mean to say my custom inits, I meant to say my custom convenience creators.

computergeek6
2008.08.10, 07:07 PM
I appreciate the offer, Willem, but I don't really feel comfortable giving out my physical location. Thanks anyway!

What's the difference between "NSString *foo", "NSString* foo", and "NSString * foo"?

Hairball183
2008.08.10, 07:43 PM
There isn't one.

When compiled, the lines

NSString *foo;
NSString* foo;
NSString * foo;

All boil down to:

NSString*foo.

IMHO, NSString *foo; is easiest to read.

computergeek6
2008.08.10, 07:49 PM
Thanks. I like it that way, and the tutorial I'm using says "NSString* foo" and I was just wondering if it was safe to change.

Willem
2008.08.10, 08:00 PM
What's the difference between "NSString *foo", "NSString* foo", and "NSString * foo"?
Questions at this level would indicate that you would benefit from some basic tutorials in C itself, removing Objective-C and Cocoa entirely from the picture. Maybe get a beginners C book first? There are tons of C tutorials on the web as well!

computergeek6
2008.08.10, 08:22 PM
I know some C, but I'm just not sure if Obj-C changes the way pointers are handled.

Willem
2008.08.10, 08:48 PM
Think of Objective-C as a super set of C. It does everything C does with a bunch of stuff added on top. Sort of the same as C++ or C#. Same base layer with goodies added.

computergeek6
2008.08.10, 08:54 PM
Ok. I read somewhere that it was a subset of C++, so I wanted to make sure I didn't break anything. Thanks for clearing that up.

AnotherJake
2008.08.10, 10:36 PM
I read somewhere that it was a subset of C++,
Absolutely not! Obj-C has nothing to do with C++ as a language. It is *compatible* with C++.

BTW, not that it matters, but my personal preference for pointers is:

MyPointer *pointer;

As already mentioned, pointers are the same in Obj-C as they are in C, but I'd like to add that not only are pointers used the same way, the entire language relies on them at its very core. id is a pointer, which means all objects are represented as pointers. It is a good idea to remember that fact, because it can be helpful when you drop into C and want to call back to an Obj-C instance.

NSString *string;

string is a pointer.

computergeek6
2008.08.10, 11:37 PM
How do I tell which methods are convenience methods, so I know when to explicitly retain objects?

AnotherJake
2008.08.10, 11:48 PM
As has been said, you need to study up on it. The link ThemsAllTook put up is the authority: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Concepts/ObjectOwnership.html#//apple_ref/doc/uid/20000043-BEHDEDDB

Quick rule of thumb: Anything you create yourself with an alloc (or new or copy) is retained. Everything else should be assumed to be autoreleased, and therefore you need to retain it if you are going to keep it for anything more than immediate temporary use (like within the current method).

This does not count @"some string" because that is an NSString string constant and is in memory and cannot be removed, like any other constant.

In the code you posted, there is a lot of:

op = [op stringByAppendingString: @"blah"];

Which should be:

op = [[[op autorelease] stringByAppendingString:@"blah"] retain];

computergeek6
2008.08.10, 11:56 PM
Thanks. I'll study that document and see if I can fix my code.