Syntax for accessing instance variables? (Objective-C)
Asked Answered
R

7

11

What is the proper syntax for accessing an instance variable in Objective-C?

Assume we have this variable:

@interface thisInterface : UIViewController {
    NSMutableString *aString;
}

@property (nonatomic, retain) NSMutableString *aString;

and that it is synthesized.

When we want to access it, we first would want to allocate and initialize it. Having programmed in Objective-C for about a month now, I've seen two different forms of syntax. I've seen people do simply aString = [[NSMutableString alloc] initWithString:@"hi"], where they allocate the string like that; I've also seen people start it off with self.aString and then they proceed to initialize their ivar. I guess I'm just trying to figure out what is the most proper way of initializing an instance variable, because with the former example, I have received EXC_BAD_ACCESS errors from it. After prepending the self. though, it didn't appear.

Forgive me if this is a duplicate question, but after reading some posts on SO, it's made me curious. I'm trying to learn the proper syntax with Objective-C because I prefer being proper rather than sloppy.

Ridicule answered 15/6, 2011 at 15:22 Comment(1)
You should use Copy instead of Retain when dealing with strings.Distance
M
16

If you have declared a property and @synthesize it in the .m file, you simply set it like this:

self.aString = @"hi"; // or [[NSMutableString alloc] initWithString:@"hi"];

Using self.varName takes advantage of what your property declaration actually does- it handles retention of the new value (since your property has the retain attribute), releasing the old value, etc for you.

If you just do:

aString = someValue;

... you may be leaking the original value that was in aString, since without using self.aString you are accessing the variable directly vs through the property.

Madelyn answered 15/6, 2011 at 15:30 Comment(6)
This is exactly what I was looking for! I figured it had to do with something in regards to the retain property. So when assigning a value to the ivar directly, we don't release the old value (since that's what the retain property does, right)? Which can end up causing some leaks. I just want to make sure that's right before I start analyzing my code.Ridicule
Yes, if you modify an ivar directly, you bypass the property's synthesized accessor code. This can cause leaks, early deallocation, etc.. bad things. Generally, that's not what you want.Madelyn
You do realise that self.aString = [[NSMutableString alloc] initWithString:@"hi"]; is a leak if the property has a retain attribute....Distance
I see. Thanks so much, your explanation was concise and easy to understand! :) And I do agree with @Simon Lee, since it would be retained twice.Ridicule
Yep- @Simon Lee is right for if you alloc instead of using an autoreleased convenience method.Madelyn
That's not an instance variable, that's a property. Different things.Vow
V
12

Note the difference between self->varName and self.varName

The first is pointer access. The second is property access.

Why is that important? Pointer access is direct. Property access, on the other hand makes use of getters and setters (be they @synthesized or not). Moreover, as a convenience, the @synthesized accessors take care of the memory mangement for you (i.e. when using self.varName = ...;), whereas varName = ...; does only what it says, i.e. the assignment -> (there lies the explanation for EXC_BAD_ACCESS errors you might be getting).

Syntactically, both forms are correct. If you want to better communicate intent, use self->varName when you want to work directly with the pointer and use self.varName when you want to take advantage of the @property convenience.

Veterinarian answered 15/6, 2011 at 15:46 Comment(1)
Excellent outline. Can you please go it a bit in detail about what could go wrong if we just do varName = ...;Skintight
C
7

Here are all the possible combinations (I think) OKs and BADs are only correct when aString property has retain attribute:

@property (nonatomic, retain) NSMutableString *aString;

So:

1

aString = [[NSMutableString alloc] init]; //OK: 

This is OK but only in the case aString is not pointing to an invalid object or you will loose a reference to that object and it will leak because you won't be able to reach it to release it.

2

aString = [NSMutableString string]; //BAD

Bad because you are suppose to retain aString (as you declared it that way), you are not retaining it and you will get surely get EXC_BAD_ACCESS in the future

3

aString = [[NSMutableString string] retain]; //OK

Same as the first approach, only good if aString is not pointing to a valid object. However I will use the first though.

4

aString = [[[NSMutableString alloc] init] autorelease];//BAD

Same as the second approach.

5

self.aString = [[NSMutableString alloc] init]; //BAD!!

Bad because you are retaining it twice, hence it will lead to memory leaks

6

self.aString = [[NSMutableString string]; //******GOOD!******

This is probably the safest. It will be retained by the property setter and since you are using the setter any other object that could have been pointed by aString will be released appropriately

7

self.aString = [[NSMutableString string] retain]; //BAD

This is retained twice.

8

self.aString = [[[NSMutableString alloc] init] autorelease];//Ok

This is also OK, but I would use the convenience method instead of this long approach :)

Be wary that the #1 and #3 options are perfectly good if you know what you are doing. In fact I use them much more frequently than #6

Collodion answered 15/6, 2011 at 15:34 Comment(1)
Thanks for your solution! I appreciate you listing the different combinations, this helps a lot! I tend to use #1 the most often, but I didn't realize that 6 would work as well.Ridicule
C
2

I personally prefer to use the self. syntax. It just makes it easier to determine that its an instance variable, and not just some other variable in the current scope that will be lost when its NSAutoreleasePool is drained. However, it is correct to use them both ways, and if you are receiving EXC_BAD_ACCESS errors, it is not because you accessed it without using self.. You are correct in saying that you must alloc it, and whichever way you choose to access your variables, keep it consistent or you will receive errors.

I hope this helps.

Cormac answered 15/6, 2011 at 15:30 Comment(0)
B
2

Always use accessors except in init, dealloc and in accessors themselves. Doing this will save you a lot of headaches like the one you're describing. Also, name your ivars something different than your property (_foo, foo_, mFoo, but not foo).

self.foo is precisely the same as [self foo]. I calls the method foo. self.foo = x is precisely the same a [self setFoo:x]. It calls the method setFoo:. If you synthesized the property foo as a retain variable, then this looks something like:

@synthesize foo = foo_;

- (void)setFoo:(id)value {
  [value retain];
  [foo_ release];
  foo_ = value;
}

This correctly releases the old value of foo_, assigns a new one and retains it.

foo = x (assuming foo is an ivar) does not call any method. None. It just assigns the value of the pointer in x to the pointer in foo. If foo pointed to something that was retained, it's leaked. If the new value you're assigning isn't retained, you'll crash later.

The solution to this is to always use accessors when you can.

Balk answered 15/6, 2011 at 15:38 Comment(1)
Thank you very much! Great summary, it makes sense. My Objective-C books/resources really didn't say much about this, which is why I was somewhat confused. Thanks again!Ridicule
H
1

Either.

Using the dot syntax is cleaner (to some) and it compiles to the equivalent. i.e self.iVar is the same as [self iVar] and self.iVar = aValue is the same as [self setIVar:aValue];

Haiduk answered 15/6, 2011 at 15:26 Comment(8)
Thats not what he was asking, he was questioning whether it should have self in there at all.Cormac
I see. I've heard though that if you tried doing iVar = aValue, it's incorrect, since it wouldn't be 'setting' the value properly. Is this true? I think this was a problem I had, but I never really understood it.Ridicule
hmm isn't translated to [self getIvar] ?Galcha
No. self.iVar is basically [self iVar]. iVar = aValue is a direct assignment. No retain or copy happens in a direct assignment.Drucilla
@bioffe. KVC requires accessors for var to be -var to retrieve the value and -setVar: to set the value. Cocoa method names with get are used to return an value by reference. e.g. getComponents from NSColorHaiduk
@Deepak where did I say anything apart from what you said? So why are you saying no?Haiduk
@Haiduk I was referring to @bioffe's statement. But now thinking about it I might've misread his statement too. Sorry about that. Should've tagged whom I was addressing.Drucilla
@Deepak - Ah. I see what you mean. Sorry.Haiduk
S
0

self.aString is a syntactic sugar to [self aString]. Synthesize a property just create the -aString and -setAString: method (depending on the property you have chosen it while not be the trivial affectation).

Now the question is whether to use the . notation. I suggest you not to use it. Why? First know that Objective-C aim to be just an addition to C. This mean that every valid C code is also a valid Objective-C code.

Now look at what they have done with the dot notation. The last statement does not hold anymore. You wont distinguish between an access to a field of a C structure and sending objective-c method.

So please don't use the dot notation. Prefer using the [self ..].

Snowcap answered 15/6, 2011 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.