Observations & Comments & Questions on Ch 19


#1

Some observations and suggestions for improving Chap 19.

p 118 Creating an Asset class. 3rd paragraph, second sentence. It would help if you added a few words to the end of the sentence: "When an object is being deallocated it is sent the message dealloc automatically by the (part of the system that does this)

Use dissimilar names for the classes and the pointer variables. I found the use of “assets”, “persons” and “employees” as pointer names confusing when I was also trying to think about the interdependencies between the classes named “Asset”, “Person”, and “Employee”. So instead of:

Employee *person = [[Employee alloc]init], maybe…
Employee *awesomeCoder = [[Employee alloc]init]

I don’t think the bit about using @class instead of #import added much of value to the learning objectives of the chapter. It was an unnecessary distraction that didn’t improve my understanding of object instance variables.

The explanation of %@ on p119 could be better. I read this at least a dozen times and I’m still not sure I understand the explanation. Although the code was pretty straightforward. This leads me to my last comments, and that’s about the output.

It’s clear that the first part of the output (e.g., <Employee 0: $0 in assets>) comes from the “description” method in the Asset class. Furthermore, given that the output shows each member of the array of employees, it also leads me to conclude that the output is generated in a loop. The problem I have is that I don’t see any calls to the “description” method, and I don’t see any loops apart from the ones creating and filling the arrays.

Is the “description” method like “dealloc”? Are they both called automatically?

Does “NSLog(@“Employee: %@”, employees);” somehow create this output?

Since (I’m guessing) the dealloc call is being made automatically by (part of the system?) I can see where the deallocation messages comes from, but I think spending a little time explaining how and why the output is created would really help novice programmers like myself. The last 5 lines of the program (not including “return 0”) aren’t in a loop, an explanation of how precisely the output is generated by this code would be welcome.

My $0.02 (so far).

I really like this book. It’s rare to find something that’s clear, brief, simple AND human.


#2

I have been going through similar issues. Until this point, I thought the author explained things well. But Chap. 19 is something else. Before doing the Challenge, I am dealing not only with the issues you mentioned, but why the author had to use the word “Asset” three different ways. He could have made his instance variables more meaningful names, like Asset *laptop.
Also, what really is disconcerting is how he puts part of his output in main.m, then switches the rest of his output to employee.m, and even more confusing, with its own assets array.
I think I’m ready to start the Challenge, but I’m making it simpler.


#3

If there’s one objective we have, it’s to remove as much ‘magic’ as possible from Objective-C. The more something feels like magic, the less penetrable and understandable it feels.

So let’s demystify some things:

-dealloc: When an object’s retain count reaches zero, the object actually calls -dealloc on itself! By the time you get to the end of the next chapter, Preventing Memory Leaks, you’ll have seen a bit about how reference counting is used so that any given object knows when to deallocate. Essentially, when any object is told to decrement its retain count, it checks to see if the new value is <= 0, and if it is, the object calls [self dealloc];.

-description: There’s nothing magical here, either. The implementation of NSLog() (like the implementation of its ancestor, printf()) scans the passed-in string for instances of substitution tokens, such as “%f” or “%@”. It then substitutes in the additional arguments in the order in which they were passed. When it encounters these tokens, NSLog() knows how to format the corresponding value based on the token itself. It sees the “%@” token, and knows that the corresponding value should be a pointer to an Objective-C object, and sends that object the -description message. It then substitutes in the string that the object returns.

I hope that helps.

No text can anticipate all of the types of questions that might crop up, so please don’t hesitate to lean on us! We want to help as much as possible.


#4

In the Employee.m code assets is declared in an unusual way.

[code]#import “Employee.h”
#import “Asset.h”

@implementation Employee

@synthesize employeeID;

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

      // Create the array
      assets = [[NSMutableArray alloc] init];
    

    }
    [assets addObject:a];
    }
    [/code]

Notice
" // Create the array
assets = [[NSMutableArray alloc] init];
"

My question is: Why is it not declared in the same way that other objects are declare in the preceding chapters, for example

why not like this

" // Create the array
NSMutableArray *assets = [[NSMutableArray alloc] init];
"

This question was answered by ibex10, however I fail to understand the answer.
I can not see where it is already declared as a NSMutableArray in the Employee class.

Can someone please elaborate on this a little more.

Any answers would be greatly appreciated.

P.S. I’m very thankful to have come across BNR and their books, thanks for helping me to understand.


#5

I believe the reason it is done this way is to follow the model/recipe/paradigm/pattern/whatever that you don’t actually allocate memory until you need it. So, you don’t initialize that array until there is actually something to put in it. That may seem trivial here, since it is a contrived example, but it could become more important in a limited memory situation such as in an iOS device.


#6

Thank you for the response.
I do understand why the allocation is made this way, in that the memory is only allocated when the class is call to create the object called assets.
Sorry I’m very new to this so i may not be using the correct terminology, I’ll try to rephrase the question.

What Im have trouble understanding, is why there is no “NSMutableArray *” typecast in front of the variable name assets in the statement below.

from Employee.m

[code]@implementation Employee

@synthesize employeeID;

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

      // Create the array
      assets = [[NSMutableArray alloc] init];
    

    }
    [assets addObject:a];
    }
    [/code]

It seems that when ever an object is created that the compiler needs to know which class the instance is being created from.

In this case assets is an object and an instance of NSMutableArray, but how does it know this when “NSMutableArray *” is missing from the statement?


#7

You nailed it. The term for this pattern is “Lazy Loading”. By lazily loading the array, we can help to keep our memory footprint to a minimum in our application.


#8

Hi MikeyWard, Are you responding to my post?
If you are have a look at it again, as you must have missed my question.

Anyone else care to elaborate, I think this is important and an explanation seems to be missing from chapters 1 through 19.

Any answers are greatly appreciated.


#9

Apologies, that was a reply to bcarter’s last post.

So, if I read your question properly, you’re looking for the declaration of the variable called assets, into which we store the NSMutableArray pointer.

Indeed, the assets variable isn’t declared in this file at all. It was declared in Employee.h:

@interface Employee : Person { int employeeID; NSMutableArray *assets; }

For our purposes, there are four different ways to declare a variable:

1) Declare it explicitly as an instance variable of a class. This is how assets was declared.

2) Declare it explicitly as a local variable. This is a declaration that appears inside of a function or method, such as:

- (void)foo { NSString *myString = @"Hello"; // followed by other stuff where we actually get around to using the local string variable }

3) Declare it explicitly as a global variable. This is a declaration that appears near the top of the file, outside of the @interface/@implementation blocks:

[code]#import “Foo.h”

NSMutableArray *globalArray;

@implementation Foo
// rest of implimentation[/code]

4) Declare it implicitly using a property. If you declare a property for an instance variable, you need not also have an explicit declaration for it (see #1).

In our case, the assets variable was declared in the @implementation in Employee.h, and then defined in -addAssetsObject: in Employee.m. You cannot assign an instance variable in a header; you can only declare its existence. In the implementation file we actually provide the instance variable with its new value (if it doesn’t already have one).

Once a variable has been declared, you need not ever cast it or specify the pointer type unless you intend to store a pointer to an object of a different type into that variable. This is because Objective-C is almost entirely untyped as a language. The compiler takes variable declarations as hints from you as to what type of object a given pointer will end up pointing to. But the compiler doesn’t actually care.

The compiler is willing to let you store an NSString pointer into a variable originally typed as NSArray. You could declare every pointer in your program as being of type “id” rather than the NSString, etc. This provides our code with a great amount of flexibility, but also enables an entire new class of bugs to arise if you wield said power incorrectly.


#10

I found it, I’ve been struggling for hours on this, its all good, because I’ve been pouring over and over, and in the end I found it, yes indeed it is declared in the header file.
I came back here to the forum just to post what I found, and see that you have responded.

I actually went so far as to create a new variable and check for nil in the same manner, basically I did a cut and paste job of the block of code with a few tweaks to make it unique. I then got the undeclared error, and realized I must have missed something. I then opened the Employee.h file and there it was. How the hello did I miss this.

Anyhow I thank you for your answer and now will read it thoroughly.

You guys are great.

P.S. Im loving this book, and I’ve already placed an order for IOS programming 3rd edition.


#11

I also discovered that objects declared inside a for loop structure don’t exist outside the structure. For example:

Looking at the line of code

NSLog(@"somekind of Array: %@", randomEmployee);

in the following

This peace of code yields an Undeclared identifier error for the object “randomEmployee”

[code] // Create 10 assets
for (int i = 0; i < 10; i++) {

        // 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];
        
    }
   
    /* Call the Body Mass Index method 
    float BMI = [person bodyMassIndex];
    NSLog(@"\n\nEmployee %d has a BMI of %.2f\n\n", [person employeeID], BMI);
    */
    NSLog(@"somekind of Array: %@", randomEmployee);

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

[/code]

While this peace of code is good

[code] // Create 10 assets
for (int i = 0; i < 10; i++) {

        // 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];
        
        NSLog(@"somekind of Array: %@", randomEmployee);
    }
   
    /* Call the Body Mass Index method 
    float BMI = [person bodyMassIndex];
    NSLog(@"\n\nEmployee %d has a BMI of %.2f\n\n", [person employeeID], BMI);
    */
    
    NSLog(@"Employees: %@", employees);

[/code]


#12

Absolutely correct. A variable only lasts as long as the scope in which it is declared.