Super Self – Initializing
หลังจากเรื่องที่แล้วได้เขียนเกี่ยวกับ Polymorphism ถ้าจะไม่พูดถึง super – self ก็คงจะไม่ครบสักเท่าไหร่
- super
เป็น keyword ที่เอาไว้อ้างอิงถึง คลาสที่สืบทอดมา - self
ก็คือเป็นการอ้างอิงถึงตัวเอง
เอาละทีนี้ก็คือว่า มันมีความสำคัญอะไรกับ 2 สิ่งนี้ สำหรับ self กับ super
จริงๆแล้ว self กับ super ส่วนมากจะได้ใช้จริงๆ กับ initial ซะส่วนมาก คือหลังจากการ alloc แล้วเราต้องทำการ init ให้มัน ทีนี้มันก็เกิดปัญหาที่ว่า ถ้าที่เราเขียน class ที่เป็น child จาก parent ที่ต้องการ initial ก่อนใช้งาน แล้วจะทำอย่างไร ?
ตัวอย่าง Code ข้างล่างจะไม่มีปัญหา เพราะว่า Child นั้นไม่ต้องการเขียน method ชื่อ init ดังนั้นก็ไปใช้ของ parent ได้
#import <Objc/Object.h> // ---- Children Class ----- @interface Children :Object { int m_age; } - (void) init:(int) age; @end // ---- Student Class ----- @interface Student :Children { float m_studentID; } @end
แบบนี้ก็อาจจะง่าย เวลาใช้งาน ก็อาจจะเขียนประมาณนี้
ChildClass* child = [[ChildClass alloc] init:12];
ก็ตอนนี้ก็เท่ากับว่า ค่า m_age นั้นมีค่าเท่ากับ 12
แต่ถ้าสมมติว่า initial ของ child ต้องการ parameter ด้วย เช่นว่า Children ก็ต้องการให้รับ age ตอน init
และ Student ก็ต้องการให้มี studentID ในตอน init ด้วยละ ดังเช่นตัวอย่าง
#import <Objc/Object.h> // ---- Children Class ----- @interface Children :Object { int m_age; } - (void) init:(int) age; @end // ---- Student Class ----- @interface Student :Children { float m_studentID; } - (void) init:(float) studentID; @end
เราจะเขียน code ในลักษณะแบบนี้
ChildClass* child = [[ChildClass alloc] init:12.00];
มันก็จะไปเรียก init ของ child และในตอนนี้ m_studentID ก็มีค่าเท่ากับ 12.00 แล้ว m_age ละ ? เราจะทำการ set ค่า initial สำหรับ parent class ได้อย่างไร ?
เราจะรู้ได้อย่างไรว่า parent class นั้นมี member data อะไรอยู่ในนั้นบ้าง ?
เพราะในขณะนี้เรา overriding ไปแล้ว มันก็เป็นการเรียกใช้ init ของ child โดยไม่ได้เรียก init ของ parent
เพื่อความเข้าใจ ลองมาดูตัวอย่างอีกสักอันกันดีกว่า
// Car.h #import <Objc/Object.h> @interface Car:Object { int m_speed; } - (void) init; - (void) setSpeed:(int)speed; - (int) getSpeed @end @implementation Car; - (void) setSpeed:(int) speed { m_speed = speed; } - (void) init { m_speed = 20; } - (int) getSpeed { return m_speed; } @end
แล้วเราก็เขียนในส่วนของ main แบบนี้
#import "car.h" int main() { Car *honda = [[Car alloc] init]; printf("Honda speed:", [honda getSpeed]); return 0; }
ผลจากการ โปรแกรมก็จะได้ว่า
Honda speed: 20
ความจริงแล้วจาก code ข้างบนถ้าทำการ compile มันก็จะไม่แจ้ง error อะไร แต่มันจะแจ้งwarning ออกมา ว่า warning: multiple methods named ‘-init’ found แต่จริงๆแล้วก็ทำงานได้ปกติ เหตุผลทีมันแจ้งแบบนี้ก็เพราะว่า init นั้นเป็น method ของ Object นั่นเอง และเราก็ทำการ Overriding มันไปนั่นเอง ทำให้มันเห็น ว่ามี init อยู่ 2 method
โอเคทีนี้สมมติว่าเรา อาจจะให้มี init กับ initWithSpeed ละ ? ตัวแรกไม่ต้องทำอะไร ส่วนตัวที่สอง ทำการ ใส่ speed เริ่มต้นให้เลย จะเขียนได้แบบไหน เราก็ได้ความคิดว่า งั้น init ก็ไม่ต้องไป Overriding ให้มัน แล้วเราก็เขียน initWithSpeed เพิ่มขึ้นมานั่นเอง โอเค งั้นมาดูเลย
#import <Objc/Object.h> @interface Car:Object { int m_speed; } - (void) initWithSpeed:(int) speed; - (void) setSpeed:(int)speed; - (int) getSpeed;
จะเห็นว่า เราก็แค่ เปลี่ยนจาก init ไปเป็น initWithSpeed เท่านั้นเอง งั้นส่วนของ implement นั้นก็ควรจะเป็น
- (void) initWithSpeed:(int)speed { m_speed = speed; }
แบบนี้ก็เป็นอันจบ แล้วส่วน main ก็น่าจะเขียนแบบนี้
Car *honda = [[Car alloc] initWithSpeed:20];
แต่ความเป็นจริงแล้ว มันไม่ได้ง่ายขนาดนั้น !!!!
ถ้า compile จริงๆมันจะเกิด error ขึ้น เพราะว่า initWithSpeed นั้น return void โอเค เราจะแก้ปัญหานี้ได้โดยการเขียน code ใน main แบบนี้
Car*honda = [Car alloc]; [honda initWithSpeed:20];
แต่ความจริงแล้ว การแก้ปัญหาแบบนี้มันไม่ค่อยถูกเท่าไหร่ เพราะว่าถ้าจะพิจารณาให้ดีแล้ว มันเหมือนกับเราเขียนว่า initWithSpeed เป็นแค่ function ที่่เรียกใช้หลักจากการสร้าง honda แล้วมากกว่า ถ้าจะสรุปง่ายก็คือว่าการเขียนแบบข้างบน ก็เหมือนกับ การเขียนแบบนี้
Car *honda = [[Car alloc] init]; [honda initWithSpeed:20];
เอาละ งั้นเราจะเขียนให้มันใช้ initWithSpeed ร่วมกับ alloc ได้ยังไง
ไม่ยากเลย เราก็ให้ initWithSpeed ไม่ต้อง return void ให้มัน return Car ออกแทนสิ แต่จะให้มัน return ได้ยังไง นี่คือปัญหา และ self กับ super จะได้เอามาใช้ตอนนี้เหละ งั้นก็ดูกันเลย
เราทำการเปลี่ยน ในส่วนของ interface ก่อน
โดยเราทำการเปลี่ยนให้ initWithSpeed ทำการ return id ออกมาแทน
- (id) initWithSpeed:(int) speed;
และในส่วนของ implementation นั้น จะเขียนได้แบบนี้
- (id) initWithSpeed:(int) speed; { [super init]; m_speed = speed; return self; }
จะเห็นว่า เราทำการ เรียกใช้ super และ self
- [super init]
ในส่วนนี้เป็นการบอกว่า ให้ เรียกใช้ Class แม่ (super) และทำการ เรียกใช้ init เพราะว่า จริงๆแล้ว Car นั้นสืบทอดมาจาก Object และ init นั้นก็เป็น method ที่เรียกใช้เวลา ทำการ alloc ตัว object ก็เป็นอันว่า เราได้ทำการ init เรียบร้อยแล้ว ต่อไปก็ - m_speed = speed
ตรงนีก็ไม่ได้มีอะไรพิเศษนอกจากทำการ ใส่ค่าให้กับตัวแปร m_speed - return self
ตรงนี้สำคัญเพราะเมื่อเราทำการ initตัวแม่แล้ว และเราก็ทำการ ใส่ค่าให้กับตัวแปรที่ต้องการแล้ว แน่นอนว่า การ alloc นั้น ต้องการขอจอง memory สำหรับตัวมันเอง ไม่ใช่ตัวแม่ ดังนั้นเราก็ return ตัวมันเองออกไป
และทีนี้เราก็จะเขียน main โปรแกรมได้ใหม่ว่า
#import "car.h" int main() { Car *honda = [[Car alloc] initWithSpeed:50 ]; Car *toyota = [[Car alloc] init]; printf("Honda speed:", [honda getSpeed]); printf("Honda speed:", [toyota getSpeed]); [honda release]; [toyota release]; return 0; }
แบบนี้ก็จะไม่เกิด error หรือ warning ใดๆ เลย และถ้าไม่อยาก initWithSpeed ก็ทำได้เหมือนกัน
ผลจากการ run โปรแกรมก็จะออกมาได้ว่า
Honda speed: 50
Toyota speed: 0
ลองโหลด source ไปลองดูได้ครับ


