When to use [self]


#1

Looking at the example on page 120

In the file Employee.h

#import "Person.h"
@class Asset;

@interface Employe : Person
{
    int EmployeID;
    NSMutableArray *assets;
}

@property int employeeID;
- (void)addAssetsObject:(Asset *)a;
- (unsigned int)valueOfAssets;

@end

Then in the file Employee.m

#import "Employee.h"
#import "Asset.h"

@implementation Employee

@synthesize EmployeeID;

- (void)addAssetsObject:(Asset *)a
{
    // Is assets nil?
    if (!assets) {

        // Create the array
        ...
}

I noticed that the *assets pointer is declared as an instance variable, but why is there no accessor method to access the instance variable directly.
For example, in the Employee.m file we check

...
if (!assets) {
...

but shouldn’t we declare a property in the .h file and then do the following in the .m file instead

...
if (![self assets]){
...

I thought that the purest always use accessor methods to interact with instance variables.

The reason I ask is because the first example of Chapter 20 is written exactly the way I think the above code should be written:

Asset.h File

...
Employee *holder;
...

@property (strong) Employee *holder;
...

Asset.h File

...
@synthesize label, resaleValue, holder;
...
if ([self holder]) {
...

So am I missing something?
Any input would be appreciated.

Thanks in advance.


#2

No, you are not missing anything. The book is showing you what’s possible; that is, that instance variables can be accessed directly (accessing instance variables directly incurs less cost than accessing them through properties.)


#3

Indeed, the better pattern is to use your accessors everywhere other than -init, -dealloc, and the accessors themselves.

But in this chapter we’re throwing lots of new knowledge at folks, especially those new to programming, so it isn’t quite necessary to talk about that just yet. :slight_smile:

~Mikey


#4

Hi,

Although I’m getting the hang of using self I still have a question after I experimented around a little with the following NSString-implementation in Asset.m.

I took this code:

- (NSString *)description { // Ist holder nicht nil? if ([self holder]) { return [NSString stringWithFormat:@"<%@: $%d, assigned to %@>", [self label], [self resaleValue], [self holder]]; }else{ return [NSString stringWithFormat:@"<%@: $%d unassigned>", } }

and changed it to this

[code]- (NSString *)description
{
{
// Is holder non-nil?
if (holder)
{
return [NSString stringWithFormat:@"<%@: $%d, assigned to %@>", label, resaleValue, holder];
} else

    {
        return [NSString stringWithFormat:@"<%@: $%d unassigned>", label, resaleValue];
    }
}

}[/code]

I received the same results in both cases. Therefore my question, why did we use “self” in this case?


#5

When you use self, you are accessing a property indirectly; that is, a method is being invoked to access the value of the property. The method can, at its discretion, compute the value of the property without even using a corresponding backing variable. (You can have properties without backing variables.)

In your examples, you have backing variables and your accessor methods are just returning the values of those variables without any modifications. That’s why you are getting the same result in both cases. You would not get the same result if your accessors took the values from the backing variables, modified and then returned them.

Accessor methods sometimes do other things beside storing and returning values. Therefore better to use accessor methods to access properties whenever harmless to do so.


#6

So, if I understand you correctly the fact that I’ve defined the ivars which I use in the NSString-method like this:

[code]//
// Assets.h
// BMI_Self
//
// Created by Serge Zehnder on 13.12.12.
// Copyright © 2012 Serge Zehnder. All rights reserved.
//

#import <Foundation/Foundation.h>

@class Employee;
@interface Asset : NSObject

{
NSString *label;
__weak Employee *holder;
unsigned int resaleValue;
}
@property (strong) NSString *label;
@property (weak) Employee *holder;
@property unsigned int resaleValue;

@end

[/code]
means that because I explicitly created backing variables the result is the same?

If that’s correct, why do I still get the same result if I remove these backing ivars?

[code]@interface Asset : NSObject

{
}
@property (strong) NSString *label;
@property (weak) Employee *holder;
@property unsigned int resaleValue;

@end
[/code]

Maybe you could give me a specific example in regards to the usage of these ivars and the NSString-method where the result would be different.
I’ve read on “stackoverflow” that the second example of this code is where [quote]Objective-C implicitly creates the backing variable for you.[/quote]

I’ve researched my e-book version of the “Objective-C Programming”-book and the only two instances where the “implicit” creation of a variable is discussed is of course in regards to “self” [quote]Inside any method, you have access to the implicit local variable self. self is a pointer to the object that is running the method. It is used when an object wants to send a message to itself. [/quote] and very early in the book in regards to the an X-Code warning -> [quote] (Ignore the warning about an implicit declaration for now.) [/quote] on pages 139 and 49 respectively.

I feel like I’m on the verge of understanding the use of “self” but somehow there seems to be a syntactical logic that escapes me.


#7

Read the following quotation.

Yes, self is an implicit local variable; it is actually a hidden parameter of the function that implements an Objective-C method.

Now imagine that there is a class called MyCelestialObject.

First quickly walk through this code and then look at the output it produces:

//  main.m

@interface MyCelestialObject: NSObject
{
    NSString *_name;
}
- (id)initWithName:(NSString *)v;
- (NSString *)name;

@end

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        NSString *name = @"Mars";
        MyCelestialObject *B1 = [[MyCelestialObject alloc] initWithName:name];
        NSLog (@"---> %s: name: %@ B1: %p", __PRETTY_FUNCTION__, name, B1);
        
        name = @"Jupiter";
        MyCelestialObject *B2 = [[MyCelestialObject alloc] initWithName:name];
        NSLog (@"---> %s: name: %@ B2: %p", __PRETTY_FUNCTION__, name, B2);

        // Observe
        long N = 7;
        while (N--)
        {
            NSLog (@"Observing heavenly bodies: %@ & %@", B1, B2);
        }
    }
    return 0;
}


@implementation MyCelestialObject

- (id)initWithName:(NSString *)v
{
    self = [super init];
    if (self)
    {
        _name = [v mutableCopy];
    }
    NSLog (@"---> %s: _name: %@: self: %p", __PRETTY_FUNCTION__, _name, self);
    return self;
}

- (NSString *)name
{
    return [_name uppercaseString];
}

- (NSString *)description
{
    long state = arc4random_uniform (2);
    
    if (state)
        return [NSString stringWithFormat:@"%@", _name];
    else {
        return [NSString stringWithFormat:@"%@", [self name]];
    }
}

@end

Now look at this output:

2013-03-15 14:30:43.170 self[33817:403] ---> -[MyCelestialObject initWithName:]: _name: Mars: self: 0x10e2131d0
2013-03-15 14:30:43.172 self[33817:403] ---> int main(int, const char **): name: Mars B1: 0x10e2131d0
2013-03-15 14:30:43.172 self[33817:403] ---> -[MyCelestialObject initWithName:]: _name: Jupiter: self: 0x7ffb08e016b0
2013-03-15 14:30:43.173 self[33817:403] ---> int main(int, const char **): name: Jupiter B2: 0x7ffb08e016b0

2013-03-15 14:30:43.173 self[33817:403] Observing heavenly bodies: Mars & JUPITER
2013-03-15 14:30:43.173 self[33817:403] Observing heavenly bodies: MARS & Jupiter
2013-03-15 14:30:43.174 self[33817:403] Observing heavenly bodies: Mars & Jupiter
2013-03-15 14:30:43.174 self[33817:403] Observing heavenly bodies: MARS & Jupiter
2013-03-15 14:30:43.175 self[33817:403] Observing heavenly bodies: Mars & JUPITER
2013-03-15 14:30:43.175 self[33817:403] Observing heavenly bodies: Mars & JUPITER
2013-03-15 14:30:43.176 self[33817:403] Observing heavenly bodies: MARS & JUPITER

Can you explain why?

  1. The addresses (in hex) on the first and second lines are the same;
  2. Those on the third and fourth lines are also the same;
  3. The names of the planets sometimes appear in upper-case letters.

If you can, then you have understood what self is and does.


#8

[b]Can you explain why?

  1. The addresses (in hex) on the first and second lines are the same;
  2. Those on the third and fourth lines are also the same;[/b]

Because in both cases the pointer _name and self are pointing to the same object in the memory. But the “self”-pointer has to send a message to the binitWithName:(NSString *)-method [/b] in order to access the information on the stack. (I presume it’s the stack and not the heap since we are taking about local and global variables/pointers).

And since “many Objective-C programmers are quite religious about accessor methods” I should also always use self, even though there might be other ways of creating a functioning code.
I added this to simply confirm and understand that we are talking about a style-convention which doesn’t necessarily mean that a code wouldn’t work. Depending on what the code is supposed to do of course. Which was one of the reason the whole thing was so confusing.

3. The names of the planets sometimes appear in upper-case letters.

Within the “- (id)initWithName:(NSString *)v”-method you create a mutable copy of the _name-pointer, which will be changed in the if/else-statement depending on the value of “long”-integer.
I’ve looked up the arc4random_uniform-function and it read -> “arc4 number generator”. What escaped me was, how this influenced the state-variable to be true or false. In what way are the generated numbers true or false?


#9

[quote]
[b]Can you explain why?

  1. The addresses (in hex) on the first and second lines are the same;
  2. Those on the third and fourth lines are also the same;[/b]

Because in both cases the pointer _name and self are pointing to the same object in the memory.[color=#FF0000] But the “self”-pointer has to send a message to the (id)initWithName:(NSString *)-method in order to access the information on the stack. (I presume it’s the stack and not the heap since we are taking about local and global variables/pointers).
[/color][/quote]
The answer in the first statement is correct, but I don’t understand what you mean by the second statement.

Neither style nor being religious about accessor methods is of importance. The importance lies in understanding the fact that there is a difference between accessing an instance variable directly and accessing it indirectly; to understand this look at the method - (NSString *)name, which does not immediately return the information stored in the instance variable but changes the information before returning it. It is very important that you understand this difference; that is, accessor methods can do other things beside merely storing and returning information.

Extract from man arc4random:

That is, the value of the state variable will be sometimes 0 (false) and sometimes 1 (true).


#10

Thanks a lot, I think I’ve got it now, and it feels like the whole language has now opened up much more. As to your comment [quote] [color=#FF0000]but I don’t understand what you mean by the second statement.[/color][/quote]

I meant to say that in the “(id)”-method “self” is sent to access the object. But that’s garbage on my part, I misread something.

The most important part of your example was that you included the NSLog statement which printed out the addresses in hex.