Objective-C Programming Part IV

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 ได้

แบบนี้ก็อาจจะง่าย เวลาใช้งาน ก็อาจจะเขียนประมาณนี้

ก็ตอนนี้ก็เท่ากับว่า ค่า m_age นั้นมีค่าเท่ากับ 12

แต่ถ้าสมมติว่า initial ของ child ต้องการ parameter ด้วย เช่นว่า Children ก็ต้องการให้รับ age ตอน init
และ Student ก็ต้องการให้มี studentID ในตอน init ด้วยละ ดังเช่นตัวอย่าง

เราจะเขียน code ในลักษณะแบบนี้

มันก็จะไปเรียก init ของ child และในตอนนี้ m_studentID ก็มีค่าเท่ากับ 12.00 แล้ว m_age ละ ? เราจะทำการ set ค่า initial สำหรับ parent class ได้อย่างไร ?
เราจะรู้ได้อย่างไรว่า parent class นั้นมี member data อะไรอยู่ในนั้นบ้าง ?
เพราะในขณะนี้เรา overriding ไปแล้ว มันก็เป็นการเรียกใช้ init ของ child โดยไม่ได้เรียก init ของ parent

เพื่อความเข้าใจ ลองมาดูตัวอย่างอีกสักอันกันดีกว่า

แล้วเราก็เขียนในส่วนของ main แบบนี้

ผลจากการ โปรแกรมก็จะได้ว่า

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 เพิ่มขึ้นมานั่นเอง โอเค งั้นมาดูเลย

จะเห็นว่า เราก็แค่ เปลี่ยนจาก init ไปเป็น initWithSpeed เท่านั้นเอง งั้นส่วนของ implement นั้นก็ควรจะเป็น

แบบนี้ก็เป็นอันจบ แล้วส่วน main ก็น่าจะเขียนแบบนี้

แต่ความเป็นจริงแล้ว มันไม่ได้ง่ายขนาดนั้น !!!!

ถ้า compile จริงๆมันจะเกิด error ขึ้น เพราะว่า  initWithSpeed นั้น return void  โอเค เราจะแก้ปัญหานี้ได้โดยการเขียน code ใน main แบบนี้

แต่ความจริงแล้ว การแก้ปัญหาแบบนี้มันไม่ค่อยถูกเท่าไหร่ เพราะว่าถ้าจะพิจารณาให้ดีแล้ว มันเหมือนกับเราเขียนว่า initWithSpeed เป็นแค่ function ที่่เรียกใช้หลักจากการสร้าง honda แล้วมากกว่า ถ้าจะสรุปง่ายก็คือว่าการเขียนแบบข้างบน ก็เหมือนกับ การเขียนแบบนี้

เอาละ งั้นเราจะเขียนให้มันใช้ initWithSpeed ร่วมกับ alloc ได้ยังไง

ไม่ยากเลย  เราก็ให้ initWithSpeed ไม่ต้อง return void ให้มัน return Car ออกแทนสิ แต่จะให้มัน return ได้ยังไง นี่คือปัญหา  และ self กับ super จะได้เอามาใช้ตอนนี้เหละ งั้นก็ดูกันเลย

เราทำการเปลี่ยน ในส่วนของ interface ก่อน

โดยเราทำการเปลี่ยนให้ initWithSpeed ทำการ return id ออกมาแทน

และในส่วนของ implementation นั้น จะเขียนได้แบบนี้

จะเห็นว่า เราทำการ เรียกใช้ 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 โปรแกรมได้ใหม่ว่า

แบบนี้ก็จะไม่เกิด error หรือ warning ใดๆ เลย และถ้าไม่อยาก initWithSpeed ก็ทำได้เหมือนกัน

ผลจากการ run โปรแกรมก็จะออกมาได้ว่า

Honda speed: 50
Toyota speed: 0

ลองโหลด source ไปลองดูได้ครับ

Objective-C Programming Part III(2).

Dynamic Binding

คือไม่รู้ว่าจะอธิบายง่ายๆได้อย่างไรว่ามันคืออะไร แต่เอาเป็นว่า ขอยกตัวอย่าง Dynamic Binding ให้เห็นภาพได้ว่า สมมติว่า เรามี class ชื่อว่า Dog และ Cat โดนทั้งสอง class นี้เป็น child ของ Animal และมี function ชื่อว่า eat เหมือนกัน ดู code ตัวอย่างภาษา C ต่อไปนี้

ถ้าสมมติว่า เราเรียก funciton_eat โดยส่ง pointer ของ Cat และ dog มาให้ ในลักษณะแบบนี้

จะเห็นว่า เราสามารถส่ง ทั้ง Cat และ Dog ไปให้ function_eat ได้ เพราะว่าทั้ง Cat และ Dog นั้นเป็น Animal ทั้งคู่ ปัญหาก็คือว่าโปรแกรมจะรู้ได้ยังไงว่าควรจะไปเรียก eat ของ cat หรือว่าไปเรียก eat ของ dog ตรงนี้ไม่ต้องกังวลเพราะว่าโปรแกรมจะไปตรวจสอบเองว่า Animal ที่ได้รับมาเป็น dog หรือ cat กระบวนการที่โปรแกรมสามารถจำแนกได้ว่าเป็น class ใดนั้นเหละคือ Dynamic Binding

id Type

ในภาษา objective-c นั้นจะมีตัวแปรชนิดพิเศษที่ชื่อว่า id ตัวแปรชนิดนี้มันจะคล้ายๆลักษณะ pointer ในภาษา C แต่ id มันสามารถ เก็บค่าอะไรก็ได้ !!!

มาดูตัวอย่างอาจจะพอเห็นภาพมากกว่า

คือจาก code ข้างบน ประกาศ class ไว้คือ Human กับ Animal และทั้งสอง class นี้ก็ไม่ได้เกี่ยวกันแต่อย่างได
มาดูที่ main โปรแกรมจะเห็นว่า ประกาศ ตัวแปรมา 3 ตัวก็คือ both , h , a ซึ่งทั้งสามตัวแปรนี้เป็นคนละชนิดกัน จุดที่อยากให้รู้ก็คือว่า เราประกาศ both เป็น id สามารถเก็บอะไรก็ได้ และ

  • เราก็ลองมาให้ both มีค่าเท่ากับ h หลังจากนั้นก็ลองเรียก printHuman
  • เสร็จแล้ว ก็ให้ both มีค่าเท่ากับ a แล้วก็เรียก printAnimal

จะเห็นว่ามันสามารถสลับเปลี่ยนไปเป็น Human หรือ Animal และเรียก function ของทั้งสองได้ ผลลัพธ์ที่ได้ก็จะเป็นแบบนี้

และการใช้งานลักษณะแบบนี้ก็เป็น dynamic binding นั่นเอง ก็อาจจะเกิดคำถามที่ว่า ก็ในเมื่อมันเก็บได้ทุกอย่าง เราก็ประกาศให้มันเป็น id หมดเลยก็ได้ คำตอบก็คือได้ แต่ไม่ควรทำ ด้วยหลายๆเหตุผล ขอยกตัวอย่างง่ายๆว่า

กับ

สองอย่างนี้ อะไรเข้าใจง่ายกว่า ว่า a คืออะไร?
ระหว่างบอกว่า a เป็น animal กับ a เป็น id ?
จริงๆมันก็มีอีกหลายๆเหตุผลที่ว่า เราควรใช้ในสิ่งที่จำเป็นมากกว่า ขอยกตัวอย่าง อีกสักอัน

สมมติว่าจาก code ตัวอย่างแรก เรามาแก้ให้เป็นแบบนี้

จะเห็นว่า เราให้ both นั้นเป็น human แต่เรากลับไปเรียกใช้ printAnimal ซึ่งไม่มีใน Human แน่นอน … แต่เวลา Compile นั้นโปรแกรมจะไม่สามารถตรวจสอบได้ว่า มันไม่มี printAnimal โปรแกรมจะสามารถ Compile ได้ปกติโดยไม่เกิด error เพราะว่า dynamic binding นั้นจะเกิดขึ้นตอนโปรแกรมเริ่มทำงาน ดังนั้นถ้าเราเรียกใช้โปรแกรมดังกล่าว ก็จะเกิด error ทันที
และจะได้ผลลัพธ์ในลักษณะแบบนี้

ฉนั้นการใช้ก็ควรจะระวังกันสักนิด

Objective-C Programming Part III Source Files

Objective-C Programming Part III.

Polymorphisms

Polymorphisms สรุปง่ายๆว่าเป็นการอนุญาติให้ derived class ( child class ) สามารถใช้ function ของ parent ได้ สำหรับ polymorphisms นั้นเห็นเด่นชัดมากที่สุดก็คงจะเป็น Overriding / Overloading

  • Overriding
    เป็นการเขียน function ของ Child class ใช้เองโดยไม่ต้องใช้ function ของ Parent Class ถ้าจะยกตัวอย่างง่ายๆ ก็เป็นต้นว่า ถ้าเรามี class ชื่อว่า Car แล้ว Car นั้นมีฟังชั่นขื่อว่า drive สมมติว่าเราจะเขียน class ใหม่ขึ้นมาโดยสืบทอด (inherit) มาจาก Car โดยชื่อว่า Tank มีฟังชั่นชื่อว่า drive เหมือนกัน แต่ว่า class ทั้งสองนั้น Drive ทำงานไม่เหมือนกัน คือพูดง่ายๆว่า รถยนต์ก็ ขับ(drive)ได้ รถถังก็ ขับ(drive) ได้ แต่วิธีการที่จะขับไม่เหมือนกัน
  • Overloading
    เป็นการเขียน Function ชื่อเหมือนกัน ใน class เดียวกัน แต่รับ parameter คนละอย่างกัน เช่นว่า เรามี class ชื่อว่า Rectangle และมี function ในการคำนวนพื้นที่ เราอาจจะต้องการให้มันรับ parameter ได้ทั้ง int และ float ในลักษณะแบบนี้เราก็จะใช้ overloading เข้ามาให้เกิดประโยชน์

อาจจะงง เล็กน้อย สรุปสั้น Overriding ก็คือ child class ที่มีการเขียน function ที่ชื่อเหมือนกับ parent ขึ้นมาเองโดยไม่ต้องสืบทอดจาก parent และ Overloading คือ method ที่สามารถรับ parameter ได้หลายๆรูปแบบ

แต่มาดู code อาจจะเข้าใจง่ายกว่า

และในส่วน implement

จากตัวอย่างข้างบนเป็นการ แสดงให้เห็นถึง Overloading คือ จะเห็นว่า Car นั้นมี Function ชื่อเหมือนกัน นั่นคือ drive แต่รับ parameter มาไม่เหมือนกัน โดยที่ drive แรกนั้น ไม่ได้รับ parameter มาเลย ส่วนตัวที่สอง รับ int เข้ามาและ ก็ในส่วนการทำงานของแต่ละฟังชั่นก็ต่างกัน

เวลาเรียกใช้เราอาจจะเขียนได้ว่า

ส่วนผลลัพธ์ที่ออกมาก็จะเป็น

ทีนี้เรามาดู Overriding กันบ้าง ก็อย่างที่บอกไปแล้วว่า เราสารมารถเขียน ให้ child class ทำงานต่างจาก parent โดยที่ ขื่อเหมือนกันได้มาดู กันเลยดีกว่า

ก็จะเห็นว่าใน class tank นั้นไม่มีอะไรเลย คือ inherit มาจาก car อย่างเดียว แต่เราสารมารถเขียน implement ของตัวเองโดยอิงจาก parent ได้

ก็เป็นอันเสร็จ คือเรา เขียน funtion drive ขึ้นมาใหม่ แทนที่ ตัวเก่าที่มีอยู่ใน Car

และก็มาลองเรียกใช้งานกันดู

ส่วนผลลัพธ์ที่ได้ก็จะได้ว่า

จากผลลัพธ์ก็จะเห็นว่า Tank drive ที่ออกมา โปรแกรมจะไปเรียก function ใน class Tank

ส่วนบรรทัดสุดท้าย ตัวโปรแกรมจะไปเรียกใช้ parent ของ tank นั่นก็คือ Car เพราะเนื่องจากว่า tank นั้น Overriding เพียงแค่ฟังชั่น

แต่สำหรับ

ไม่ได้ทำการ overriding

มันจึงไปใช้งาน parent ของมันแทน

เพิ่มเติม http://gd.tuwien.ac.at/languages/c/c++oop-pmueller/
และ http://developer.apple.com/documentation/Cocoa/Conceptual/OOP_ObjC/Introduction/chapter_1_section_1.html

Talk: Mac Pro

จริงว่าจะไม่เขียนเรื่องส่วนตัวเท่าไหร่ แต่ก็คิดว่าถ้ามันเกี่ยวกับ mac ก็น่าจะเขียนบ้าง คือเนื่องจากวันก่อนผมเข้าไปดูที่เวปของ Apple ( ปกติไม่ค่อยเข้า ) ก็เห็น Spec ของ Mac Pro จริงๆก็ออกได้ตั้งแต่ช่วงมกราคมแล้วละ เห็นแล้วก็ร้องเพราะว่า ความแรงมันต่างกันกับเครื่องปัจจุบันเหลือเกิน คือเครื่องปัจจุบันของผม ก็ Mac Pro น่ะ Xeon Quard core , Ram 4 Gb.

ก็น่าจะเรียกได้ว่าเทพพอสมควร แต่ว่าพอเห็นเครื่อง Mac Pro ใหม่แล้วก็ต้องบอว่าของผม มันเด็ก เพราะว่าตัวใหม่มัน Xeon 8-Core ขอกรี๊ดสักที ให้ตายเหอะ ราคาก็เท่ากันอีกต่างหาก คือเพิ่งได้เครื่องมาตอน พฤษศจิกายน มามกราคม มันออกใหม่แรงกว่าเดิม ในราคาเท่าเดิม นี่มันระยะเวลาแค่ 3 เดือนเองน่ะ แต่ก็เอาเถอะไม่คิดมาก เพราะว่าไม่ใช่เงินเรา บริษัทออกให้ แต่ก็แอบเซ็งเล็กน้อย แต่ก็อย่างว่างานส่วนมากของผมคือ เขียนโปรแกรม บน Mac เนี่ยสิ งานเลยไม่ได้ยุ่งกับ Graphic มากเท่าไหร่ แต่ว่า ก็ต้องใช้เครืองแรงๆ เพราะว่าโปรแกรมที่เขียนมันเกี่ยวกับ 3D ใช้แต่ Xcode แต่ก็ยังดีใจอย่างว่า อีกราวๆ 2 อาทิตย์ บริษัทจะซื้อ Mac book pro มาด้วย ไว้ให้มันได้มาก่อนจะได้เอามายลโฉม เจ้า MBP

Pure Virtual

คือจาก เมื่อวันที่ผ่านมา เนื่องจากว่า งานที่ทำอยู่มีส่วนต้องให้ port โปรแกรมจาก Win มาสู่ mac และบังเอิญว่า ผม compile แล้วมันผ่าน แต่ว่าตอน Runtime มันดัน Error สิ่งที่มันบอกมาก็คือว่า pure virtual method called terminated called with out an active exception ปัญหาที่เกิดขึ้น ก็ยัง งงๆๆว่าเป็นเพราะอะไร แต่พอหลังจากให้เพื่อนมาดูแล้วก็นั่งถกกัน ว่ามันเกิด เฉพาะกับ mac หรือเปล่าเพราะว่าใน win ไม่เห็นมันจะเกิด ก็เลยทำการเขียน code ที่ทำงานแบบเดียวกัน ก็ัดังตัวอย่างข้างล่าง

ก็ถ้า ดูตาม code ก็จะเห็นว่า มี Base กับ Derived เนื่องจากว่า Base Class มันเป็น Pure Virtual (Abtract Class) เราต้อง implement ส่วนของ code การทำงานใน derived class เสมอไม่อาจจะเขียน implement ใน Base Class ได้ และปัญหาจากโปรแกรมข้างบนเกิดขึ้นเพราะในขณะที่ base class ได้เรียก destructor ของตัวเองนั้น มันกลับไปเรียกใช้งานฟังชั่นใน derived class ด้วย ซึ่งนั่นก็ทำให้มันเกิด error เพราะว่า derived class ได้ถูกทำลายไปก่อนแล้ว ทำให้ไม่สามารถเรียกได้

ลองดูตรง

จาก destructor นี้จะเห็นว่า มีการเรียกฟังก์ชั่น foo และแน่นอนว่า foo ไปเรียก

อีกที สืบเนื่องจากว่ามันเป็น pure virtual แปลว่าตัว Base Class เองไม่อาจจะเขียน implement code ได้มันจึงต้องไปเรียก banana ในตัว Derived Class ที่มีการเขียน implement code และ Derived นั้นได้ทำลายจาก destructor ไปเรียบร้อยแล้ว มันเลยไม่เจอ banana ทำให้เกิด error นั่นเอง

ลองศึกษา เพิ่มเติมเกี่ยวกับ Virtual Function ได้ที่ http://en.wikipedia.org/wiki/Virtual_function

เขียนโปรแกรมกันเถิดจะเกิดผล Programming and technology