Can someone explain "self" a little better to me please?

Discussion in 'iOS Programming' started by Tander, Jun 26, 2013.

  1. Tander macrumors 6502a

    Tander

    Joined:
    Oct 21, 2011
    Location:
    Johannesburg, South Africa
    #1
    Hi all,

    So this morning I took on the first homework set by the CS193p Stanford iOS programming course (Winter 2013)

    The homework was to make the card "flip" through different suit / ranks.

    Now this is how I first tried the challenge:

    Code:
    #import "CardGameViewController.h"
    @interface CardGameViewController ()
    @property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
    @property(nonatomic) int flipCount;
    @property (nonatomic, strong) PlayingCardDeck *deck;
    
    
    @end
    
    @implementation CardGameViewController
    @synthesize deck;
    
    - (PlayingCardDeck *)deck
    {
        if (!deck) deck = [[PlayingCardDeck alloc]init];
    
        return deck;
    }
    -(void)setFlipCount:(int)flipCount
    {
        _flipCount = flipCount;
    
        self.flipsLabel.text = [NSString stringWithFormat:@"Flips: %d", self.flipCount]; 
    }
    
    - (IBAction)flipCard:(UIButton *)sender
    {
        sender.selected = !sender.isSelected;
        [sender setTitle:[_deck drawRandomCard].contents forState:UIControlStateSelected];
        self.flipCount++;
        NSLog (@"%@", [self.deck drawRandomCard].contents);
    }
    
    @end
    
    So the property Playingcard deck inherits from another class deck - which has the method drawRandomCard.

    Deck inherits from another class Card.

    Now the above code would give my NSLog statement (null) and for some reason and I couldn't access my deck property directly without the
    Code:
    @synthesize deck;
    This version of the code works perfectly:

    Code:
    #import "CardGameViewController.h"
    @interface CardGameViewController ()
    @property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
    @property(nonatomic) int flipCount;
    @property (nonatomic, strong) PlayingCardDeck *deck;
    
    
    @end
    
    @implementation CardGameViewController
    
    
    - (PlayingCardDeck *)deck
    {
        if (!_deck) _deck = [[PlayingCardDeck alloc]init];
    
        return _deck;
    }
    -(void)setFlipCount:(int)flipCount
    {
        _flipCount = flipCount;
    
        self.flipsLabel.text = [NSString stringWithFormat:@"Flips: %d", self.flipCount]; 
    }
    
    - (IBAction)flipCard:(UIButton *)sender
    {
        sender.selected = !sender.isSelected;
        [sender setTitle:[self.deck drawRandomCard].contents forState:UIControlStateSelected];
        self.flipCount++;
        NSLog (@"%@", [self.deck drawRandomCard].contents);
    }
    
    @end
    

    My understanding of self:

    It calls the object calling the method. Its calling itself.

    Questions:

    1. Why did the first version of the code not work?
    2. Can someone try explain self a little better for me? :(

    If I had understood self - I would have completed the homework all on my own - however I took a look at an example and I was only missing the self keyword...so close!! :mad::(
     
  2. Menneisyys2 macrumors 603

    Joined:
    Jun 7, 2011
    #2
    When self is used together with strictly (in the .m file) synthesized properties, it makes sure the auto-generated accessor / mutator methods are used and you're not directly, without going thru the generated methods, assigning values.

    When not using ARC, ALWAYS use self when assigning a value to an object reference - it makes sure the system makes sure the previous content, if any, is released, and the new one is properly retained. In ARC you should use self too.

    In earlier (3.x) Xcode versions, you didn't get compile-time errors if you forgotten adding the self prefix. Fortunately, Xcode 4.x is much more intelligent in this respect - this is why your first app doesn't compile.

    It's only in very few cases that self shouldn't be used.
     
  3. Tander thread starter macrumors 6502a

    Tander

    Joined:
    Oct 21, 2011
    Location:
    Johannesburg, South Africa
    #3
    Thanks for explaining. That helps a little.

    For some reason I was under the impression we only use self when we are referring to the class we're in. Not the property within the .m file.

    So I can access all my properties instance variables using self? (Within that .m file? )
     
  4. Menneisyys2 macrumors 603

    Joined:
    Jun 7, 2011
    #4
    Yes, and you should always do so, most importantly when assigning values to them. (For read-only access, that is, as an rvalue, self isn't strictly needed in most cases as no behind-the-curtains releases/ retains need to take place. You, nevertheless, can always prefix those properties too. I too do so.)
     
  5. Tander thread starter macrumors 6502a

    Tander

    Joined:
    Oct 21, 2011
    Location:
    Johannesburg, South Africa
    #5
    Ah okay. Starting to make sense. Without the self prefix - we would be accessing the ivars directly - bypassing the setters and getters?
     
  6. Menneisyys2 macrumors 603

    Joined:
    Jun 7, 2011
    #6
    Yup, unless you rename them in @synthesize like

    @synthesize arr=__myaarr;

    where "arr" is the property (e.g., @property (retain) NSMutableArray* arr;) and "__myaarr" is just the name of the ivar synthesized by the system when using the @synthesize directive liek above.

    If you don't rename the property (more precisely, tell the system under which name the synthesized ivar should be accessed), you'll be able to access it directly, without self. If you do add such an explicit renaming to all of your must-be-retained properties, you'll no longer be able to errorenously write

    arr = [[NSMutableArray alloc] init];

    instead of

    self.arr = [[NSMutableArray alloc] init];

    as there's no 'arr' ivar synthesized any more, but '__myaarr'. This means the latter would work (but you won't want to use it as the whole point of renaming is making sure you don't accidentally use an ivar variable name without self. in front of it)

    __myaarr = [[NSMutableArray alloc] init];
     
  7. Tander thread starter macrumors 6502a

    Tander

    Joined:
    Oct 21, 2011
    Location:
    Johannesburg, South Africa
    #7
    100% clear now why my first version didn't work at all and why we rename the synthesize variables.

    Thank you so much for taking the time to explain.

    So much to learn as a new iOS developer! :eek::cool:
     

Share This Page