*holder doesn't zero out (nil) after employee dealloc


#1

While using manual retain/release, Asset’s weak reference to Employee(*holder) doesn’t zero out (“nil”) after Employee is deallocated. When program attempts to NSLog the description of the Asset (Asset.m/dealloc) that belonged to the Employee the program spits out the following errors.

*** -[Employee respondsToSelector:]: message sent to deallocated instance 0x104114d50

Highlighting the line: Asset.m/(description method)
–> Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)

main.m

[code]#import <Foundation/Foundation.h>
#import “Employee.h”
#import "Asset.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];
// Create an instance of Employee
NSMutableArray *employees = [[NSMutableArray alloc] init];

for (int i = 0; i < 10; i++) {
    //////////////////////////////////////////////////////
    //  Clean Loop cookie crumbs
    NSAutoreleasePool *arpLocal = [[NSAutoreleasePool alloc] init];
    
    //  Create an instance of Employee
    Employee *person = [[Employee alloc] init];
    
    //  Give the instance variables interesting values
    [person setWeightInKilos:90 + i];
    [person setHeightInMeters:1.8 - (i / 10.0)];
    [person setEmployeeID:i];
    
    //  Put the employee in the employees array
    [employees addObject:person];
    
    [person release];
    person = nil;
    [arpLocal drain];
    //////////////////////////////////////////////////////
}
NSMutableArray *allAssets = [[NSMutableArray alloc] init];

//  Create 10 assets
for (int i = 0; i < 10; i++) {
    //////////////////////////////////////////////////////
    //  Clean Loop cookie crumbs
    NSAutoreleasePool * arpLocal = [[NSAutoreleasePool alloc] init];
    
    //  Create an asset
    Asset *asset = [[Asset alloc] init];
    
    //  Give it an interesting label
    NSString *currentLabel = [NSString stringWithFormat:@"Laptop %d", i];
    [asset setLabel:currentLabel];
    [asset setResaleValue:i * 17];
    
    //  Get a random number between 0 and 9 inclusive
    NSUInteger randomIndex = random() % [employees count];
    
    //  Find that employee
    Employee *randomEmployee = [employees objectAtIndex:randomIndex];
    
    //  Assign the asset to the employee
    [randomEmployee addAssetsObject:asset];
    
    [allAssets addObject:asset];
    
    [asset release];
    asset = nil;
    [arpLocal drain];
    //////////////////////////////////////////////////////
}

NSLog(@"Employees: %@", employees);

NSLog(@"Giving up ownership of one employee");
[employees removeObjectAtIndex:5];

NSLog(@"allAssets: %@", allAssets);

NSLog(@"Giving up ownership of array");

[allAssets release];
allAssets = nil;

[employees release];
employees = nil;

[arp drain];
//////////////////////////////////////////////////////////
sleep(50);
return 0;

}[/code]
CLASS: Person

[code]///////////////////////////////////
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
float heightInMeters;
int weightInKilos;
}
@property float heightInMeters;
@property int weightInKilos;

  • (float)bodyMassIndex;
    @end
    ///////////////////////////////////
    // Person.m
    #import “Person.h”
    @implementation Person
    @synthesize heightInMeters, weightInKilos;
  • (float)bodyMassIndex
    {
    return [self weightInKilos] / ([self heightInMeters] * [self heightInMeters]);
    }
    @end
    ///////////////////////////////////[/code]
    CLASS: Employee

[code]///////////////////////////////////
// Employee.h
#import “Person.h”
@class Asset;
@interface Employee : Person
{
int employeeID;
NSMutableArray *assets;
}
@property int employeeID;

  • (void)addAssetsObject:(Asset *)a;

  • (unsigned int)valueOfAssets;
    @end
    ///////////////////////////////////
    // Employee.m
    #import “Employee.h”
    #import “Asset.h”
    @implementation Employee
    @synthesize employeeID;

  • (void)addAssetsObject:(Asset *)a
    {
    // Is assets nil?
    if (!assets) {
    assets = [[NSMutableArray alloc] init];
    }
    [assets addObject:a];
    [a setHolder:self];
    }

  • (unsigned int)valueOfAssets
    {
    unsigned int sum = 0;
    for (Asset *a in assets) {
    sum += [a resaleValue];
    }
    return sum;
    }

  • (float)bodyMassIndex
    {
    return [super bodyMassIndex] * 0.9;
    }

  • (NSString *)description
    {
    NSString *result = [[NSString alloc] initWithFormat:@"<Employee %d: $%d in assets>", [self employeeID], [self valueOfAssets]];
    return [result autorelease];
    }

  • (void)dealloc
    {
    NSLog(@“deallocating %@”, self);
    [assets release];
    [super dealloc];
    }
    @end
    ///////////////////////////////////[/code]
    CLASS: Asset

[code]///////////////////////////////////
// Asset.h
#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
///////////////////////////////////
// Asset.m
#import “Asset.h”
#import “Employee.h”
@implementation Asset
@synthesize label, resaleValue, holder;

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

  • (void)dealloc
    {
    NSLog(@“deallocating %@”, self);
    [label release];
    [holder release];
    [super dealloc];
    }
    @end
    ///////////////////////////////////[/code]


#2

I presume that you have ARC turned off for this exercise?

The auto-zeroing of weak references is a runtime feature of ARC, and so if ARC is disabled, you will not get the benefit of auto-zeroing.


#3

:open_mouth: Oh! Yes I’m not using ARC.
How would you remedy this?

Would assigning the Asset pointer (*holder) to nil in “Employee/dealloc()” be okay?

[code]///////////////////////////////////
// Employee.m

  • (void)dealloc
    {
    NSLog(@“deallocating %@”, self);
    for (Asset *a in assets) {
    [a setHolder:nil];
    }
    [assets release];
    [super dealloc];
    }
    [/code]
    Also, since the tool compiled without error and Product/Analyze detected no issues I assume weak property declaration still exists for MRR (Manual Retain Release) it just doesn’t zero it’s deallocated references. Correct?

Thanks. :slight_smile:


#4

You got it!

Correct - the __weak keyword existed well before ARC, but had a slightly different usage and meaning.


#5

Thanks! :smiley: