More on Classes Inheritance Copyright © 2012 by Yong-Gu Lee
More on classes
Managing classes on separate files Two files for each class (one for the interface and one for the implementation) Can be too granular
Main file // // main.m // FractionTest // // Created by 용구 이 on // Copyright (c) 2012 년 All rights reserved. // #import #import "Fraction.h" int main (int argc, const char * argv[]) { Fraction *myFraction = [[Fraction alloc] init]; // set fraction to 1/3 [myFraction setNumerator: 1]; [myFraction setDenominator: 3]; // display the fraction NSLog value of myFraction is:"); [myFraction print]; } return 0; }
Menu for new class
Class name
Fraction.h
Fraction.m
Synthesized Accessor Methods You can have your setter and getter methods (collectively known as accessor methods) automatically generated for The first step is to use directive in your interface section to identify your properties. These properties are often your instance variables. In the case of our Fraction class, the two instance variables numerator and denominator fall into this category.
@property // // Fraction.h // FractionTest // // Created by 용구 이 on // Copyright (c) 2012 년 All rights reserved. // Fraction : int numerator, denominator; -(void) print; -(double)
@synthesize // // Fraction.m // FractionTest // // Created by 용구 이 on // Copyright (c) 2012 년 All rights reserved. // #import numerator, denominator; -(void) print { NSLog numerator, denominator); } -(double) convertToNum { if (denominator != 0) return (double) numerator / denominator; else return NAN;
Accessing Properties Using the Dot Operator [myFraction numerator] myFraction.numerator The general format here is: instance.property You can use a similar syntax to assign values as well: instance.property = value This is equivalent to writing the following expression:[instance setProperty: value] Previously, you set the numerator and denominator of your fraction to 1/3 using the following two lines of code: [myFraction setNumerator: 1]; [myFraction setDenominator: 3]; Here’s an equivalent way to write the same two lines: myFraction.numerator = 1; myFraction.denominator = 3;
Multiple Arguments to Methods A method to set both the numerator and the denominator could be named and used like this: [myFraction setNumerator: 1 andDenominator: 3]; [myFraction setTo: 1 over: 3];
Example // // Fraction.h // FractionTest // // Created by 용구 이 on // Copyright (c) 2012 년 All rights reserved. // Fraction : int numerator, denominator; -(void) print; -(double) convertToNum; -(void) setTo: (int) n over: (int)
// // Fraction.m // FractionTest // // Created by 용구 이 on // Copyright (c) 2012 년 All rights reserved. // #import numerator, denominator; -(void) print { NSLog numerator, denominator); } -(double) convertToNum { if (denominator != 0) return (double) numerator / denominator; else return NAN; } -(void) setTo: (int) n over: (int) d { numerator = n; denominator = d;
// // main.m // FractionTest // // Created by 용구 이 on // Copyright (c) 2012 년 All rights reserved. // #import #import "Fraction.h" int main (int argc, const char * argv[]) { Fraction *myFraction = [[Fraction alloc] init]; [myFraction setTo: 100 over: 200]; [myFraction print]; [myFraction setTo: 1 over: 3]; [myFraction print]; } return 0; }
Methods Without Argument Names When creating the name for a method, the argument names are actually optional. For example, you can declare a method like this: -(int) set: (int) n: (int) d; Note that, unlike in previous examples, no name is given for the second argument to the method To invoke the set:: method, you use the colons as argument delimiters, as shown here: [aFraction set: 1 : 3]; -(void) setTo: (int) n over: (int) d
Local variables Variables defined within a method is local and destroyed upon exit from the method -(void)reduce { int u = numerator; int v = denominator; int temp; while (v != 0) { temp = u % v; u = v; v = temp; } numerator /= u; denominator /= u; }
The static Keyword static int hitCount = 0; -(int) showPage { static int pageCount = 0;... ++pageCount;... return pageCount; }
The local static variable would be set to 0 only once when the program started and would retain its value through successive invocations of the showPage method. Note the difference between making pageCount a local static variable and making it an instance variable. In the former case, pageCount could count the number of pages printed by all objects that invoked the showPage method. In the latter case, the variable would count the number of pages printed by each individual object because each object would have its own copy of pageCount.
The self keyword -(void) print { [self reduce]; NSLog numerator, denominator); }
Inheritance
It all begins at the Fraction: The class that has no parent is at the top of the hierarchy and is known as a root class. Parent class/Superclass Child class/Subclass
Example #import // Simple example to illustrate inheritance #import // ClassA declaration and ClassA: NSObject { int x; } ClassA -(void) initVar { x = 100; // Class B declaration and ClassB : ClassA ClassB -(void) printVar { NSLog = %i", x); int main (int argc, const char * argv[]) { ClassB *b = [[ClassB alloc] init]; [b initVar]; // will use inherited method [b printVar]; // reveal value of x; } return 0; } public
Rectangle Rectangle: int width, height; -(int) area; width, height; -(void) setWidth: (int) w andHeight: (int) h { width = w; height = h; } -(int) area { return width * height; } -(int) perimeter { return (width + height) * 2;
int main (int argc, char *argv[]) { Rectangle *myRect = [[Rectangle alloc] init]; [myRect setWidth: 5 andHeight: 8]; NSLog w = %i, h = %i", myRect.width, myRect.height); NSLog = %i, Perimeter = %i", [myRect area],[myRect perimeter]); } return 0; } [Switching to process 2263 thread 0x0] :33: porg5[2263:707] Rectangle: w = 5, h = :33: porg5[2263:707] Area = 40, Perimeter = 26
Square (inherits Square: Rectangle -(void) setSide: (int) s; Square: Rectangle -(void) setSide: (int) s { [self setWidth: s andHeight: s]; } -(int) side { return width; int main (int argc, char *argv[]) { Square *mySquare = [[Square alloc] init]; [mySquare setSide: 5]; NSLog s = %i", [mySquare side]); NSLog = %i, Perimeter = %i", [mySquare area], [mySquare perimeter]); } return 0; } :38: porg5[2329:707] Square s = :38: porg5[2329:707] Area = 25, Perimeter = 20
A Point Class and Memory Allocation Rectangle: int width, height; -(int) area; -(int) perimeter; -(void) setOrigin: (XYPoint *) pt; -(XYPoint *)
@interface XYPoint: NSObject { int x; int y; int x, y; -(void) setX: (int) xVal andY: x, y; -(void) setX: (int) xVal andY: (int) yVal { x = xVal; y = Rectangle { XYPoint *origin; } -(void) setOrigin: (XYPoint *) pt { origin = pt; } -(XYPoint *) origin { return origin;
Main int main (int argc, char *argv[]) { Rectangle *myRect = [[Rectangle alloc] init]; XYPoint *myPoint = [[XYPoint alloc] init]; [myPoint setX: 100 andY: 200]; [myRect setWidth: 5 andHeight: 8]; myRect.origin = myPoint; NSLog w = %i, h = %i", myRect.width, myRect.height); NSLog at (%i, %i)",myRect.origin.x, myRect.origin.y); NSLog = %i, Perimeter = %i", [myRect area], [myRect perimeter]); } return 0; } :43: porg5[2743:707] Rectangle w = 5, h = :43: porg5[2743:707] Origin at (100, 200) :43: porg5[2743:707] Area = 40, Perimeter = :43: porg5[2743:707] Square s = :43: porg5[2743:707] Area = 25, Perimeter = 20 Program ended with exit code: 0
Classes not owning their objects int main (int argc, char *argv[]) { Rectangle *myRect = [[Rectangle alloc] init]; XYPoint *myPoint = [[XYPoint alloc] init]; [myPoint setX: 100 andY: 200]; [myRect setWidth: 5 andHeight: 8]; myRect.origin = myPoint; NSLog at (%i, %i)", myRect.origin.x, myRect.origin.y); [myPoint setX: 50 andY: 50]; NSLog at (%i, %i)", myRect.origin.x, myRect.origin.y); } return 0; } :46: porg5[2796:707] Origin at (100, 200) :46: porg5[2796:707] Origin at (50, 50)
Classes owning their objects -(void) setOrigin: (XYPoint *) pt { if (! origin) origin = [[XYPoint alloc] init]; origin.x = pt.x; origin.y = pt.y; } The change to the setOrigin: method means that each Rectangle instance now owns its XYPoint instance. Even though it is now responsible for allocating the memory for that XYPoint, it should also now become responsible for releasing that memory. In general, when a class contains other objects, at times you will want to have it own some or all of those objects.
Overriding Methods You can’t remove or subtract methods through inheritance. However, you can change the definition of an inherited method by overriding it. A method defined with the same name as that of a parent class replaces, or overrides, the inherited definition. Your new method must have the same return type and take the same number and type of arguments as the method you are overriding.
-(void) printVar { // added method NSLog = %i", x); //////////////////////////// int main (int argc, const char * argv[]) { ClassB *b = [[ClassB alloc] init]; [b initVar]; // uses overriding method in B [b printVar]; // reveal value of x; } return 0; } // ClassA declaration and ClassA: NSObject { int x; } -(void) ClassA -(void) initVar { x = 100; // ClassB declaration and ClassB: ClassA -(void) initVar; -(void) ClassB -(void) initVar { x = 200; }
Abstract classes Sometimes classes are created just to make it easier for someone to create a subclass. For that reason, these classes are called abstract classes or, equivalently, abstract superclasses. Methods and instance variables are defined in the class, but no one is expected to actually create an instance from that class. For example, consider the root object NSObject. Can you think of any use for defining an object from that class? The abstract superclass gives a common interface for working with all types of subclassses.