Objective-C Method Calls counter

Discussion in 'Mac Programming' started by TheHeavySoldier, Mar 10, 2014.

  1. TheHeavySoldier macrumors newbie

    Joined:
    Mar 8, 2014
    #1
    Hey everyone,

    I'm trying to create a method that uses a static global variable to keep track of how many times a certain method is called, (in this case an add method to add 2 fractions).

    I have the following code.

    Code:
    [B][I]// Fraction.h[/I][/B]
    +(int)countAdd;
    
    +(Fraction *)addCount: (Fraction *)f;
    
    -(Fraction *)add: (Fraction *)f;
    
    [B][I]// Fraction.m[/I][/B]
    [B]+(Fraction *)addCount: (Fraction *)f {
        extern int gAddCounter;
        gAddCounter += 1;
        
        [I]return [f add: [Fraction self]];[/I] // This is wrong
    }
    [/B]
    +(int)countAdd {
        extern int gAddCounter;
        
        return gAddCounter;
    }
    
    -(Fraction *)add: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator + denominator * f.numerator;
        result.denominator = denominator * f.denominator;
        
        return result;
    }
    
    [B][I]// main.m[/I][/B]
    Fraction *myFraction = [[Fraction alloc] init];
    Fraction *myFractionB = [[Fraction alloc] init];
            
    Fraction *resultFraction;
            
    // Set fraction to 1/3
    [myFraction setNum: 1 setDi: 4];
    [myFractionB setNum: 6 setDi: 4];
            
    // Display the fraction
    printf("%i/%i + %i/%i is: \n", myFraction.numerator, myFraction.denominator, myFractionB.numerator,
                                   myFractionB.denominator);
            
    resultFraction = [myFraction addCount: myFractionB];
    [resultFraction printReduced: YES];
    
    I'm not really sure how to call the existing add method inside my new addCount method.
    If anyone can help me understand and give me a hint on how to fix it, that would be greatly appreciated :)

    Here will be my entire code:
    Fraction.h
    Code:
    #import <Foundation/Foundation.h>
    
    @interface Fraction : NSObject
    
    @property int numerator, denominator;
    
    -(void)printReduced: (bool)b;
    -(void)setNum: (int)n setDi: (int)d;
    -(void)reduce;
    
    +(int)countFractions;
    +(int)countAdd;
    
    -(double)convertToNum;
    
    +(Fraction *)allocF;
    +(Fraction *)addCount: (Fraction *)f;
    
    -(Fraction *)add: (Fraction *)f;
    -(Fraction *)subtract: (Fraction*)f;
    -(Fraction *)multiply: (Fraction *)f;
    -(Fraction *)divide: (Fraction *)f;
    
    @end
    
    Fraction.m
    Code:
    #import "Fraction.h"
    
    static int gCounter, gAddCounter;
    
    @implementation Fraction
    
    @synthesize numerator, denominator; // Name the variables numerator and denominator, instead of _numerator and _denominator, like the @property does automatically
    
    +(Fraction *)allocF {
        extern int gCounter;
        gCounter += 1;
        
        return [Fraction alloc];
    }
    
    +(int)countFractions {
        extern int gCounter;
        
        return gCounter;
    }
    
    +(Fraction *)addCount: (Fraction *)f {
        extern int gAddCounter;
        gAddCounter += 1;
        
        return [f add: [Fraction self]];
    }
    
    +(int)countAdd {
        extern int gAddCounter;
        
        return gAddCounter;
    }
    
    -(void)printReduced: (bool)b {
        if (b == YES) {
            [self reduce];
        }
        if (numerator >= denominator) {
            int properNumerator = numerator % denominator;
            int wholeNumber = numerator / denominator;
            
            if (properNumerator == 0) {
                printf("%i\n", wholeNumber);
            } else {
                printf("%i %i/%i\n", wholeNumber, properNumerator, denominator);
            }
        } else {
            printf("%i/%i\n", numerator, denominator);
    
        }
    }
    
    -(void)setNum: (int)n setDi: (int)d {
        numerator = n;
        denominator = d;
    }
    
    -(void)reduce {
        int u = numerator, v = denominator, temp;
        
        while (v != 0) {
            temp = u % v;
            u = v;
            v = temp;
        }
        
        numerator /= u;
        denominator /= u;
    }
    
    -(double)convertToNum {
        if (denominator != 0) {
            return (double)numerator / denominator;
        } else {
            return NAN;
        }
    }
    
    -(Fraction *)add: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator + denominator * f.numerator;
        result.denominator = denominator * f.denominator;
        
        return result;
    }
    
    -(Fraction *)subtract: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator - denominator * f.numerator;
        result.denominator = denominator * f.denominator;
    
        return result;
    }
    
    -(Fraction *)multiply: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.numerator;
        result.denominator = denominator * f.denominator;
    
        return result;
    }
    
    -(Fraction *)divide: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator;
        result.denominator = denominator * f.numerator;
    
        return result;
    }
    
    @end
    
    main.m
    Code:
    #import <Foundation/Foundation.h>
    #import "Fraction.h"
    
    int main(int argc, const char * argv[])
    {
    
        @autoreleasepool {
            // Allocatiing fractions
            Fraction *myFraction = [[Fraction alloc] init];
            Fraction *myFractionB = [[Fraction alloc] init];
            
            // Allocating fractions using a custom alloc method
            Fraction *countFractionA = [[Fraction allocF] init];
            Fraction *countFractionB = [[Fraction allocF] init];
            
            Fraction *resultFraction;
            
            // Set fraction to 1/3
            [myFraction setNum: 1 setDi: 4];
            [myFractionB setNum: 6 setDi: 4];
            
            // Display the fraction
            printf("%i/%i + %i/%i is: \n", myFraction.numerator, myFraction.denominator, myFractionB.numerator,
                                           myFractionB.denominator);
            resultFraction = [myFraction add: myFractionB];
            [resultFraction printReduced: YES];
            
            resultFraction = [myFraction addCount: myFractionB];
            [resultFraction printReduced: NO];
            
            resultFraction = [myFraction multiply: myFractionB];
            //[resultFraction printReduced: YES];
            
            resultFraction = [myFraction divide: myFractionB];
            //[resultFraction printReduced: YES];
            printf("Fractions allocated: %i\n", [Fraction countFractions]);
        }
        return 0;
    }
    
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    Can you explain why addCount: is a class method (starts with +) rather than an instance method (starts with -)?

    Is this something you decided to do on your own? Or is this an exercise? If so, please post the complete description from the book.

    In a class method, the keyword 'self' refers to the class object itself, not an instance of the class. For example, it refers to the Fraction class, not an object made by the Fraction class. If that doesn't make sense to you, then you need to study the difference between a class and instances of that class. This distinction is fundamental and pervasive in object-oriented programming.


    If you think that because gAddCounter is a global, then everything that refers to it must also be global (or a class method), then that's wrong. Any function or method anywhere can refer to a global variable, if it knows the name and type of that variable.


    The second problem is here:
    Code:
    static int gCounter, gAddCounter;
    
    This tells the compiler that gAddCounter is NOT a global. More specifically, the name is not global, although the variable's lifetime is independent of any object, function, or method. In short, its existence is independent, but its name isn't globally visible.

    A true global variable has BOTH an independent existence (lifetime) AND a globally visible name (scope). These two things are often confused or conflated, but they're different things.

    The use of the word 'static' in this context is what limits the visibility of the variables. If the variable isn't used in any other file, then this limitation has no effect. In effect, no one else "names the name", so the lack of global name visibility is moot.

    However, if the variable IS used in another source file, then 'static' will limit its name scope to only the file where the variable is defined, and no one else can see it. The variable will still have an independent existence (lifetime), but its name will be limited.


    I get the feeling that you're studying variable lifetime, name scope, etc. which involve the keywords 'static', 'extern', and the placement of variable declarations. However, that's just a guess, because you haven't stated what you're studying or the purpose of this exercise.
     
  3. TheHeavySoldier thread starter macrumors newbie

    Joined:
    Mar 8, 2014
    #3
    I know that the + sign says the method is a class method instead of a instance method, but I'm having a bit of a difficult time figuring out when I need a class method or instance method. I'm not really sure why I made a class method in this case, like I said, I'm just a bit confused on when to use one or the other.

    I figured that because only my Fractions class uses the gAddCounter variable, I could make it static.

    I'm reading the book "Programming in Objective-C", Chapter 10 - More on Variables and Data Types: initialising objects, scopes, enum data types, typedef, data conversions and bit operators.

    I'm also kind of confused when it comes to
    Code:
    [B]-(Fraction *)[/B]addCount:(Fraction *)f
    because I'm calling the add method I've created before, what does the first (Fraction *) refer to, would that be
    Code:
    [[B]thisInstance[/B] addCount: myFractionB];
    (If I called this method in my main.m file)

    Also, the exercise I'm doing is: 3. Add a counter to the Fraction class’s add: method to count the number of times it is
    invoked. How can you retrieve the value of the counter?
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    One simple way to know which one to write is to ask yourself this question:
    Do you want 'self' to refer to an instance of the class or not?
    If the answer is "Yes", then the method needs to be an instance method.

    Recall that an instance of Fraction is what has the instance variables 'numerator' and 'denominator'. The Fraction class itself does not.

    If you think of the class itself as the "factory" that "manufactures" instances, that's another way to know where to put the method. That is, ask yourself whether the method is being used during the manufacture of the instance, or is it being used by the manufactured object itself? If something's being used during manufacture, then that's what the class itself is doing.

    There can be other reasons for defining a class method rather than an instance method, but start with those two: "What should 'self' refer to?" and "Is it during or after manufacture?".


    That's correct, but then you don't need all the 'extern' declarations in the methods. Everything that comes after a static variable is declared can already see the declared variable. In this case, the extra externs don't cause the compiler to complain, but a programmer reading the code might well wonder what the intent was, and whether it signifies a possible latent bug.


    Suppose addCount: is defined as an instance method (i.e. the same as add: is defined). How would you call it?

    Now, suppose addCount: is defined as an instance method. How would you define it? I can think of two ways:
    1. Duplicate the code from add:, and also increment the static counter.
    2. Call [self add:f], and also increment the static counter.

    The second is simpler, because it builds on what you already have. That may be a more obvious hint than you want, but I think it's worthwhile to show why by giving an explicit contrast.


    The class method countAdd seems fine as-is, but its name is too easily confused with addCount:. I'd change countAdd to countAdded.

    Incidentally, this is an example of a class method that isn't used during manufacture, yet is still perfectly reasonable as a class method. There's a single counter for how many adds have been done, that all Fraction instances have in common. There's a single Fraction class that all Fraction instances have in common. This commonality is logical to place in the class itself, rather than in each instance.


    Strictly speaking, you're not following directions. The exercise clearly says to add the counter to the existing add: method, NOT to create another method that counts additions. If you'd done this as given, the question of whether to make addCount: a class or instance method wouldn't have arisen at all, because there wouldn't be a separate method, and I suspect an answer would have been much simpler and more obvious to create.
     
  5. TheHeavySoldier thread starter macrumors newbie

    Joined:
    Mar 8, 2014
    #5
    Hmm ok, so it would be better to only use the extern declaration when I'm accessing a global variable that's in another file (in other words, not static)

    Those 2 ways to find out if you need a class or instance method are useful, thanks.

    About following the exercise, I had a feeling I wasn't supposed to add an extra method, but figured it would be a bit more of a challenge.

    I really appreciate your help, thanks :D
     

Share This Page