views:

298

answers:

4

Hi Guys,

First off, please forgive the stupidness of this question but Im not from a C/C++ background. I'm a little unclear about what the difference in roles between the .h and .m files when it comes to properties.

I understand the concept of interfaces, and I see that in part the .h file is an interface for the implementation, but what I am not clear on is this:

  • Why are properties/methods defined outside of the {} braces?
  • What am i defining in the braces when I write something like this:

    IBOutlet UITextField *numberField;

    Is this a field definition in an interface?

  • When I am adding the @Property lines to the .h files are these actual implementations of a n auto property or just an interface blueprint? If so is the @syntesis the actual implementation?

I guess my biggest confusion seems to be that if I want a property I'm defining what I need in three different places (1) in the interfaces braces, (2) as @property outside the braces and (3) with @synthesis in the .m file. This seems long winded, but its fine if I can work out what these three parts do.

Cheers, Chris.

+1  A: 
  1. methods are defined outside of the braces since the braces are meant to encapsulate the state of the object which can be argued does not include the instance or class methods.

  2. What you are defining in the braces are instance variables that can be referenced as self.ivar

  3. The @property and @synthesize directives simply setup accessors for you instance variables so you can set them by doing self.ivar = someVar. So in other words it sets up the "dot syntax" for you to use.

and to answer your finale question: To define a property or instance variable simply declare it in your .h file as a variable inside the braces. To setup accessor methods on that same property you need to do BOTH @property and @synthesize.

ennuikiller
actually, instance variables are accessed as self->ivar. only properties are accessed using self.ivar
newacct
A: 
  1. Well that is just Objective C syntax, methods and @property outside {} and variables inside {}.

  2. @property is the way of telling that you are going to write getter and setters (kind of enforcing it), but you can write getter/setter without setting them @property. @property is in .h file because its declaration. And why it is outside {}, well as i said before its just the syntax, what we can do?

  3. @synthesis will in actual implement getter and setters, if you dont synthesis but you have set them @property, you have to implement those getter and setters by your hand. And @synthesis is in .m file because its implementation.

Something more for you to read on this subject can be find here.

http://theocacao.com/document.page/510

itsaboutcode
+6  A: 

I'll answer your questions below, but perhaps the best way to learn this stuff is to read some user-friendly notes intended for folks new to the language, such as the Learn Objective-C tutorial over at cocoadevcentral.

An example

I'd like to help answer your questions with an example (I love learning by example). Let's say you're a teacher writing a program that asks students a particular yes/no question, and keeps track of how many get it correct and how many students it has asked.

Here is a possible interface for this class:

@interface Question : NSObject {
  NSString* questionStr;
  int numTimesAsked;
  int numCorrectAnswers;
}

@property (nonatomic, retain) NSString* questionStr;
@property (nonatomic, readonly) int numTimesAsked;
@property (nonatomic) int numCorrectAnswers;
@property (nonatomic) int numWrongAnswers;

- addAnswerWithTruthValue: (BOOL) isCorrect;
@end

The three variables inside the braces are instance variables, and every instance of your class will have its own values for each of those variables. Everything outside the braces but before @end is a declaration of a method (including the @property declarations).

(Side note: for many objects, it's useful to have retain properties, since you want to avoid the overhead of copying the object, and make sure it isn't released while you're using it. It's legal to retain an NSString as in this example, but it is often considered good practice to use copy instead of retain since an NSString* might actually point to an NSMutableString object, which may later change when your code expects it to stay the same.)

What @property does

When you declare a @property, you're doing two things:

  1. Declaring a setter and getter method in the class's interface, and
  2. Indicating how the setter and getter behave.

For the first one, it's enough to know that this line:

@property (nonatomic, retain) NSString* questionStr;

is basically the same as this:

- (NSString*) questionStr;                           // getter
- (void) setQuestionStr: (NSString) newQuestionStr;  // setter

in the header. You literally are declaring those two methods; you can call them directly, or use the dot notation as a shortcut to call them for you.

The "basically" part in "basically the same" is the extra info given by keywords like nonatomic and retain.

The nonatomic keyword indicates that they're not necessarily thread-safe. The common retain keyword indicates that the object retains any value that's set, and releases previous values as they're let go.

For example:

// The correct answer to both questions is objectively YES.
Question* myQuestion = [[Question alloc] init];
NSString* question1 = [[NSString alloc] initWithString:@"Is pizza tasty?"];
// question1 has retain count of 1, from the call to alloc
myQuestion.questionStr = question1;
// question1 now has a retain count of 2
NSString* question2 = [[NSString alloc] initWithString:@"Free iPhone?"];
myQuestion.questionStr = question2;
// question1 has a retain count of 1, and question2 has retain count of 2

If the @property declaration for questionStr had been assign instead, then all the myQuestion.questionStr = statements would not have made any changes at all to the retain counts.

You can read a little more about properties here.

What IBOutlet and IBAction do

These are basically no-op words which act only as a way to tell Interface Builder which pieces of the header file to pay attention to. IBOutlet literally becomes an empty string when the compiler looks at it, and IBAction becomes the void return value. We do need them to work with Interface Builder, though, so they are important -- just not to the compiler.

Quick note on C structs and arrow vs dot notation

By the way, the data part of an Objective-C object is very similar to a C struct. If you have a pointer to a C struct, you can use arrow notation -> to refer to a specific part of the struct, like this:

struct MyStructType {
  int i;
  BOOL b;
};
struct MyStructType* myStruct;
myStruct->i = 3;
myStruct->b = TRUE;  // or YES in Objective-C.

This same syntax works the same way in Objective-C:

Question* question = [[Question alloc] init];
question->questionStr = @"Is this a long answer?";  // YES

But when you do this, there is no method call happening behind the scenes, unlike the dot notation. With the dot notation, you're calling the setter (or getter if there's no = afterwards), and these two lines are the same:

question.questionStr = @"Chocolate?";
[question setQuestionStr:@"Chocolate?"];

It's often a good idea to avoid the arrow notation in favor of the dot notation, since the dot notation lets you enforce valid state -- for example, that the pointers your class has are always retained. You can even disallow others from using the arrow notation by declaring your instance variables as @private; they can still use the getter and setter to access it, if you declare a @property for it.

What @synthesize does

Now, when you get around to actually implementing your class, @synthesize says something like "make sure the getter and setter get implemented for this property." It does not say "implement both of these for me," because the compiler is polite enough to check for your own implementation first, and only fill in the pieces you've missed. You don't have to use @synthesize at all, even if you use @property out the wazoo -- you could always just provide your implementations for your setters and getters, if you're into that sort of thing.

You probably noticed in the Question interface above that there's a property which is not an instance variable (numWrongAnswers), which is fine because you're just declaring methods. In the example code here, you can see how this actually works:

@implementation Question

@synthesize questionStr, numTimesAsked, numCorrectAnswers;

- (void) setNumCorrectAnswers: (int) newCorrectAnswers {
  // We assume the # increases, and represents new answers.
  int numNew = newCorrectAnswers - numCorrectAnswers;
  numTimesAsked += numNew;
  numCorrectAnswers = newCorrectAnswers;
}

- (int) numWrongAnswers {
  return numTimesAsked - numCorrectAnswers;
}

- (void) setNumWrongAnswers: (int) newWrongAnswers {
  int numNew = newWrongAnswers - self.numWrongAnswers;
  numTimesAsked += numNew;
}

- (void) addAnswerWithTruthValue: (BOOL) isCorrect {
  if (isCorrect) {
    self.numCorrectAnswers++;
  } else {
    self.numWrongAnswers++;
  }
}

@end

One thing that's happening here is we're faking an instance variable called numWrongAnswers, which would be redundant information if we stored it in the class. Since we know numWrongAnswers + numCorrectAnswers = numTimesAsked at all times, we only need to store any two of these three data points, and we can always think in terms of the other one by using the two values we do know. The point here is to understand that a @property declaration is really just about declaring a setter and getter method, which usually corresponds to an actual instance variable -- but not always. The @synthesize keyword by default does correspond to an actual instance variable, so that it's easy for the compiler to fill in the implementation for you.

Reasons to have separate .h and .m files

By the way, the whole point of declaring methods in one file (the .h header file) and defining their implementation in another (the .m or methods file) is to help decouple the code. For example, if you only update one .m file in your project, you don't have to recompile the other .m files, since their object code will remain the same -- this saves time. Another advantage is that you can use a library that includes only header files and pre-compiled object code, or even dynamic libraries where you need the header file so the compiler is aware of which methods exist, but those methods aren't even linked in with your executable file. These advantages are hard to appreciate when you first start coding, but just the logical breakdown and encapsulation of implementation becomes useful after a short while.

I hope that's helpful!

Tyler
Some constructive criticism of an overall impressive post: (1) NSString properties should really be marked as retain, (2) I think the bit about why you want .m and .h files is not needed in this context as it's better to stick with what is going on. I liked that you talked about the purpose of IBOutlet and IBAction.
Kendall Helmstetter Gelner
Actually, NSString properties should be marked as copy.
bbum
Oops, that's what I meant - the example code already has it as retain... thanks for correcting my mistake!
Kendall Helmstetter Gelner
good comments; I added a note about NSString properties and moved the .h/.m thoughts to the end
Tyler
Extremely, impressive answer thank you.
Owen
A: 

The variables inside the brackets define the physical structure of your class. Those are the actual instance variables that store information.

The stuff outside the brackets make up the class's interface — methods and properties. A property in and of itself does not reserve any storage space or affect any variable — it just declares a generic interface for accessing something. Remember that a property doesn't have to have an underlying instance variable — for example, the totalPrice property in a ShoppingCart class might dynamically sum the prices of all the items in the cart.

Inside the implementation file, you tell the class how to actually do its work. For methods, obviously, you just supply an implementation. For a property, you can either provide accessor implementations yourself or ask it to synthesize accessors for an instance variable.

Chuck