Problem when using *allPossession as a @property


#1

Hi all,

First post here, this is a lovely book indeed, so pleased with this purchase.

So my problem is, I’m using @synthesize method to declare allPossessions, that ends up with a EXC_BAD_ACCESS error when its run, because I used self.allPossessions to check if the instance variable is nil in fetchPossessionIfNeccessary, but the first line in allPossesions getter method calls fetchPossessionIfNeccessary right back, it becomes endless loop. Yes I’m doing this part differently from the book, no issues when following the book.

Here is the code, in PossessionStore.h

@interface PossessionStore : NSObject
@property (nonatomic, strong) NSMutableArray *allPossessions;
...

In PossessionStore.m

@implementation PossessionStore
@synthesize allPossessions = _allPossessions;
...

- (NSArray *)allPossessions
{
    [self fetchPossessionsIfNecessary];
    return _allPossessions;
}

...

- (void)fetchPossessionsIfNecessary
{
    if (!self.allPossessions) { // <-- PROBLEM HERE
        // Try to get a copy of allPossession array from file
    }
    
    if (!self.allPossessions) { // <-- AND HERE
        // alloc init allPossession
    }
}

No more problems if I access the instance variable directly

- (void)fetchPossessionsIfNecessary
{
    if (!_allPossessions) { // <- No more problems 
         ...
    }

    if (!_allPossessions) { // <- ditto
        ...
    }
}

I wanted to stick to the convention that all instance variables should be accessed from accessor methods, but I can’t figure a way to do that here, or is something like this an exception to the rule?

Thanks!


#2

In Objective-C, instance variables can be accessed directly by name in any instance method of the class. AKA an instance can directly access its own iVars by name. I believe this is safe and acceptable. Your situation will not work as described because of the infinite loop you are creating
(-allPossessions calls -fetchPossessionsIfNecessary which calls -allPossessions. Although the two following examples are the same, this is best practice or else BNR wouldn’t have suggested it.

I believe the convention of accessing iVars through setters/getters refers to external “objects” and their quest to obtain specific values from an object’s iVars. I put together a small example that shows what will happen if you try to directly access iVars from a source other than the instance where the iVar is declared. I created a Person class whose interface and implementation are as follows:


//Interface File

@interface Person : NSObject
{
    NSString *firstName;
}

@property (nonatomic, copy) NSString *firstName;

@end

//Implementation File

#import "Person.h"

@implementation Person

@synthesize firstName;

@end

Next, I created an instance of the Person class in main like so:

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        
        Person *brian = [[Person alloc] init];

        NSLog(@"Using a setter/getter:");
        
        [brian setFirstName:@"Brian"];
        NSLog(@"brian's first name is: %@", [brian firstName]);
        
        NSLog(@"Directly accessing the iVar:");
        
        brian->firstName = @"Brian";
        NSLog(@"Direct Access: brian's first name is %@", brian->firstName);   
    }
    return 0;
}

This code would not build. The error I received was “Instance variable ‘firstName’ is protected”. By default, the iVars of objects you create are @protected. You can change this to either @private and @public. @protected means that the iVar can be accessed by name by the class and any subclasses. @private means that only that particular class can access the iVar by name. @public means that any object can access the iVar by name. In order to build this code so you can see the output, I changed the firstName iVar to @public like so:

@interface Person : NSObject
{
    @public
    NSString *firstName;
}

This code would then build and here is the output:

By changing the firstName iVar to @public, main() was able to access the iVar via the “->” operator. Without the @public declaration, the code failed to build. To sum, you should feel safe about accessing the iVar by name in a class where it is declared due to the default @protected nature.

As a side note, I’m not going to argue whether accessors in the -init is right or wrong, however instead of calling accessors in your -init (PRE-ARC), you could do something like this

-(id)initWithPossessionName:(NSString *)name serialNumber:(NSString *)number valueInDollars:(int)value
{
self = [super init];
if(self){
     possessionName = [name copy];
     serialNumber = [number copy]; 
     valueInDollars = value;
     dateCreated = [[NSDate date] retain];
   }
 return self;
}

Here, the iVar is set directly and through “copy” and “retain” the values are retained by the Possession instance. By creating properties, subsequent setting / getting of the values will result in proper memory management.

ARC changes this so I wouldn’t worry about it. I was merely demonstrating that accessors need not be utilized to access the instance’s own iVars in the -init and/or otherwise. Also, I’m not sure of the overhead of calling an accessor rather than directly accessing an iVar, but I imagine there is a difference albeit minimal in a small application.


#3

First of all, thanks for your reply, some of this information had left me already, so that was a bit of a refresher, and you are right accessing iVars within the class is allowed.

Thinking about the code, if I put everything in fetchPossessionIfNeccessary inside the allPossessions getter, it’ll be mostly the same, because every call to the allPosessions getter also calls fetchPossessionIfNeccessary anyways (page 265), except then it’d be weird to write self.allPossessions; on a line all by itself in createPosession to fill up the allPossessions.

[code]

  • (Possession *)createPossession
    {
    self.allPossessions; // This would look like line left in by mistake (if I merged fetchPossessionIfNeccessary with the allPossessions getter)

    [self fetchPossessionIfNeccessary]; // this looks so much more meaningful


    }[/code]
    I get it now.

Edit:
Understand more of why the authors choose direct access of iVars after reading chapter 21. And sorry for choosing this nick its really annoying.