Objective-C Programming Chapter 4

ในช่วงแรกเริ่มของคอมพิวเตอร์ การเขียนโปรแกรมด้วยภาษาเก่าๆ อย่าง C , Fortran , Assembly เป็นลักษณะการเขียนชุดคำสั่ง (function, subroutine) แบบลำดับขั้นตอนเพื่อการแก้ปัญหา โปรแกรมเมอร์มักจะนึกถึงข้อมูลและการกระทำต่อข้อมูลนั้นเป็นอันดับแรก เป็นต้นว่าจะเขียนโปรแกรมหาพื้นที่ของสี่เหลี่ยมและวงกลม สิ่งที่ต้องคิดเมื่ือเริ่มเขียนโปรแกรมก็คือจะแก้ปัญหานี้ยังไงดี ? โดยทั่วๆไปโปรแกรมเมอร์ทำอย่างแรกก็คือคิดวิธีการคำนวนหาพื้นที่ หลังจากนั้นก็เริ่มเขียนส่วนประกอบต่างๆของโปรแกรม ได้แก่ตัวแปรที่ไว้เก็บข้อมูลต่างๆเช่น ด้านกว้าง ด้านยาว พื้นที่ของสี่เหลี่ยม รัศมีของวงกลมและพื้นที่ของวงกลม เมื่อมีตัวแปรต่างๆแล้วก็เขียนคำสั่งในการคำนวนพื้นที่สี่เหลี่ยมและพื้นที่วงกลมก็เสร็จเรียบร้อย ลักษณะการเขียนโปรแกรมด้วยวิธีการคิดแบบนี้จะเรียกว่า Procedural Programming ซึ่งเหมาะสำหรับการแก้ปัญหาเฉพาะอย่างเช่น แก้ปัญหาสมการคณิตศาสตร์ หรือการคำนวนทางวิทยาศาสตร์ โปรแกรมที่เราได้เขียนไปตั้งแต่บทแรกจนถึงบทที่ 3 ก็เป็นแบบ Procedural เช่นเดียวกัน แนวความคิดการเขียนโปรแกรมแบบนี้มีข้อเสียคือเมื่อโปรแกรมมีความซับซ้อนและมีขนาดใหญ่ การจัดการต่างๆของโปรแกรมทำได้ลำบาก จนกระทั่งในปี 1970 Xerox PARC ได้พัฒนาภาษา Small Talk ขึ้นมา ภาษานี้ได้เสนอแนวคิดของการเขียนโปรแกรมเชิงวัตถุหรือที่เราเรียกว่า Object Oriented Programming (OOP) แนวความคิดนี้เป็นเรื่องใหม่ในวงการการคอมพิวเตอร์ในสมัยนั้น และได้ขยายวงกว้างออกไปอย่างรวดเร็ว เมื่อมีภาษาใหม่ๆเกิดขึ้น เช่น C++ , Java , C# และ Objective-C จึงเป็นภาษาแบบ OOP แทบทั้งสิ้น

 

แล้วอะไรคือการเขียนโปรแกรมเชิงวัตถุละ ? ถ้าคิดถึง “วัตถุ” ในชีวิตประจำวันของเราก็คงจะเข้าใจได้ไม่ยาก คอมพิวเตอร์ รถ บ้าน หมา คน หรือแทบจะทุกอย่างที่สัมผัสจับต้องได้ก็เป็นวัตถุทั้งนั้น แต่ในโลกของโปรแกรมมีความพิเศษกว่าโลกจริงเพราะทุกอย่างสามารถเป็นวัตถุได้ทั้งนั้น ไม่จำเป็นต้องเป็นสิ่งที่จับต้องได้ วัตถุในโลกของการเขียนโปรแกรมคืออะไรก็ตามที่สามารถตีกรอบแนวคิดได้ สมมติว่าเขียนโปรแกรมเล่นดีวีดี ปุ่มกดเล่นหนัง, ภาพและคำบรรยายก็เป็นวัตถุได้ หรือแม้แต่รายการเพลงโปรดก็เป็นวัตถุได้ ทุกอย่างสามารถเป็นวัตถุได้ทั้งนั้น ลองมองกลับไปที่โปรแกรมคำนวนหาพื้นที่สี่เหลี่ยมและวงกลม ถ้าใช้แนวคิด Procedural ก็ต้องคิดลำดับขึ้นในการแก้ปัญหาเป็นอันดับแรก แต่เมื่อเขียนโปรแกรม OOP เราสนใจว่าสิ่งใดคือวัตถุก่อนเป็นอันดับแรก พิจารณาจากโปรแกรมเราก็จะเห็นว่าสี่เหลี่ยมเป็นรูปทรงที่ประกอบไปด้วยความกว้าง ความยาว และพื้นที่ และวงกลมก็เป็นรูปทรงที่มีความยาวของรัศมีและมีพื้นที่เหมือนกัน บางคนอาจจะมองสองสิ่งนี้อาจจะเป็นวัตถุเดียวกันคือ “รูปทรง” หรืออาจจะเป็นคนละวัตถุกันคือ “สี่เหลี่ยม” และ “วงกลม” ก็ได้ เพราะแต่ละคนได้ให้นิยามและตีกรอบของวัตถุในโปรแกรมต่างกัน และไม่มีคำตอบที่ถูกต้องที่สุด คงพอจะมองเห็นภาพโดยรวมของการเขียนโปรแกรมเชิงวัตถุมากขึ้น และจากนี้ไปเราจะได้เรียนรู้คอนเซ็ปต่างๆ ได้ออกแบบและลงมือเขียนโปรแกรม


Class

คลาสหมายถึงแบบในการสร้างวัตถุ ยกตัวอย่างเหตุการณ์ อธิบายคลาสอย่างง่ายๆ เช่น กลางเดือนที่ผ่านมาผมกับพี่หมีไปงาน Camera Fair ที่ศูนย์ประชุมแห่งชาติสิริกิต เราสองคนอยากจะซื้อกล้องใหม่สักตัว เมื่อถึงงานก็เดินดูสินค้าตามบูทต่างๆ ผมเดินไปถึงบูท Nikon เลือกดูกล้องรุ่น D7000 พี่หมีก็อยากได้รุ่นเดียวกัน เราสองคนก็เลยซื้อกล้องรุ่นเดียวกันมา แต่กล้องของผมกับพี่หมีผลิตมาจากคนละที่กันของพี่หมีผลิตที่โรงงานญี่ปุ่น ส่วนของผมผลิตจากโรงงานไทย แต่ไม่ว่าจะโรงงานไทยหรือโรงงานที่ญี่ปุ่นก่อนจะผลิตกล้องรุ่นนี้ได้ ทุกโรงงานก็ต้องมีต้นแบบหรือพิมพ์เขียวเพื่อให้โรงงานผลิตตามสเปกได้ เราเรียกต้นแบบหรือพิมพ์เขียวว่า Class และคลาสนั้นจะประกอบไปด้วยสองส่วนที่สำคัญคือ คุณลักษณะ และพฤติกรรม

 

คุณลักษณะ

กล้องของผมกับพี่หมีเป็นรุ่นเดียวกัน แต่กล้องผมกับกล้องของพี่หมีมีสีต่างกัน ในงานพี่หมีซื้อ memory เปลี่ยนให้มีขนาดใหญ่ถึง 16 GB ในขณะที่กล้องผมยังใช้ของเดิมที่มีขนาดแค่ 8 GB แต่ผมซื้อเลนส์ใหม่เป็นขนาดที่ซูมได้มากกว่าเดิม ตอนนี้กล้องของผมก็พี่หมีมีคุณสมบัติแตกต่างกันแล้ว แต่กล้องของเราก็ยังเป็นรุ่นเดียวกันทำงานได้เหมือนกัน

 

พฤติกรรม

หลังจากซื้อกล้องมาใช้งานผมสามารถใช้กล้อง ถ่ายรูป บันทึกวีดีโอ ดูรูปที่ถ่ายไปแล้ว โอนข้อมูลจากล้องลงคอมพิวเตอร์ นอกจากนี้ตัวกล้องยังมีระบบการทำงานอัตโนมัติ เช่น กรณีแสงไม่พอกล้องจะใช้แฟรชให้โดยอัตโนมัติ จะเห็นว่ากล้องนี้มีการทำงานที่แบ่งเป็นสองอย่างคือ สิ่งที่เราสามารถกระทำกับกล้องโดยตรง และกล้องทำกับตัวกล้องเอง

 

Class

State

Behavior

กล้อง

สีดำ

ถ่ายรูป

Memory 8 GB

อัดวีดีโอ

Len Kit 18 – 105

โอนข้อมูล

แฟรชอัตโนมัติ

 

Instance & Method

โรงงานเมื่อมีแบบพิมพ์เขียวแล้วก็เวลาได้ผลิตกล้องออกมาสู่ตลาด กล้องแต่ละตัวจะถูกประกอบขึ้นมาใหม่และมีหมายเลข ตัวเครื่องไม่ซ้ำกันเลย กล้องแต่ละตัวที่โรงงานผลิตเราเรียกว่า Instance แน่นอนว่ากล้องของผมเป็น instance หนึ่ง และกล้องของพี่หมีก็เป็นอีก instance ถึงกล้องเราจะเป็นรุ่นเดียวกันทุกอย่างเหมือนกันแต่เป็นกล้องคนละตัวกัน ถือว่าเป็นคนละ Instance ถึงจะเป็นคนละ instance แต่กล้องทุกตัวที่ผลิตต้องมีพฤติกรรมหรือการทำงานที่เหมือนกันกับคลาสของมัน เช่นเมื่อเราต้องการถ่ายรูป ที่ต้องทำก็คือหันกล้องไปยังสิ่งที่ต้องการและกดที่ปุ่มชัดเตอร์ ก็จะได้ภาพที่ต้องการออกมา เราเรียกพฤติกรรมหรือการทำงานของ instance นั้นว่า method เช่นการกดชัดเตอร์

( รูป 1 Class & Instance)

เราได้รู้เกี่ยวกับ อ๊อบเจ็ก ( Instance ) , คลาส , เมธอด กันไปแล้ว ยังมีคำศัพท์ที่จะได้ยินบ่อยๆ อีก 3 คำ ซึ่งเป็นคุณสมบัติที่สำคัญของ Object คือ Encapsulation , Polymorphism และ Inheritance

Encapsulation

คือการซ่อนปกปิดสิ่งที่ซับซ้อนหรือไม่ต้องการให้เห็น เช่น เมื่อเราต้องการจะถ่ายรูป เราก็เพียงแค่เอาน้ิวกดไปที่ชัดเตอร์ กล้องก็จะทำงานและบันทึกรูปที่เราต้องการไปยังการ์ดหน่วยความจำ เราไม่ได้รู้ด้วยซ้ำไปว่ากล้องทำงานอย่างไร บันทึกรูปภาพลงหน่วยความจำได้อย่างไร กลไกลต่างๆที่สลับซับซ้อนได้ถูกซ่อนไว้ในตัวกล้อง ผู้ใช้รู้เพียงแค่กดชัดเตอร์อย่างเดียวเท่านั้น นอกจากนี้ encapsulation ยังมีข้อดีอื่นอีกเช่นเราสามารถยังใช้ method เดิมของ object นั้นได้แม้ว่ากลไกลภายในเปลี่ยนไป ยกตัวอย่างเพื่อให้เห็นภาพมากขึ้น เช่น ผ่านไปได้สี่เดือนโรงงานผลิตกล้องได้ออก firmware ใหม่ทำให้กล้องตัวเดิมบันทึกภาพมีสีสันสดใสขึ้น เราก็ไม่รู้เหมือนกันอีกว่าโรงงานทำอะไรเพิ่มเติมกับกล้องปรับแต่งอะไรไปบ้าง แต่เมื่อเราต้องการถ่ายรูปก็ยังกดชัดเตอร์ปุ่มเดิมไม่ต้องเรียนรู้อะไรเพิ่มเติมเลย

Polymorphism

คือการใช้ method แบบเดิมกับวัตถุที่ต่างกัน ยกตัวอย่างเช่น หากต้องการถ่ายรูปจากกล้องเราก็เพียงแต่มองหาปุ่มชัตเตอร์แล้วก็กดลงไปจากนั้นเราก็ได้ภาพที่เราต้องการ แล้วถ้าถ้าต้องการถ่ายรูปจากโทรศัพท์มือถือละ ? สิ่งที่ต้องทำก็เหมือนกันคือหาปุ่มชัตเตอร์แล้วก็กดถ่ายรูป ไม่ว่าจะเป็นกล้องหรือโทรศัพท์มือถือซึ่งเป็นสิ่งของคนละอย่างกันกลไกลภายในทำงานไม่เหมือนกัน แต่เราสามารถใช้การกดชัตเตอร์เพื่อถ่ายรูปได้เช่นกัน

Inheritance ( Subclass )

การสืบทอดคุณสมบัติของคลาสหรือซับคลาส ถ้าจะยกตัวอย่างให้เห็นภาพเพื่อเข้าใจง่ายๆ ก็ขอยกตัวอย่างเรื่องกล้องเหมือนเดิม เช่น เราอาจจะซื้อกล้องรุ่น X80 ต่อมาอีกสองปี บริษัทได้ปรับปรุงกล้องรุ่นนี้ให้มีคุณสมบัติเพิ่มมากขึ้นเช่นถ่ายวีดีโอได้ แล้วก็ผลิตเป็นกล้องรุ่นใหม่ที่เป็นรุ่นปรับปรุงชื่อ X90 นอกจากนี้ยังสามารถใช้อุปกรณ์เดิมของ X80 ได้ด้วย การนำเอาต้นแบบมาปรับปรุงและกลายเป็นต้นแบบอันใหม่เรียกว่า subclass คลาสที่เป็นต้นแบบเรียกว่า Parent ส่วนคลาสที่สืบทอดจากต้นแบบนี้เรียกว่า Child

Why Objective-C ?

การเขียนโปรแกรมเชิงวัตถุด้วยภาษา Objective-C มีข้อดีหลายๆอย่าง อย่างแรกก็คือภาษา Objective-C เป็นภาษาที่พัฒนาต่อจากภาษา C จึงสามารถจะเขียนโปรแกรมแบบ Procedural หรือ Object Oriented ก็ได้ อีกทั้งภาษา C ที่เป็นภาษาที่แพร่หลาย มีชุดคำสั่งและไลบรารีต่างๆมากมาย สามารถนำโค้ดที่เขียนด้วยภาษา C เหล่านั้น นำมาใช้กับ Objective-C ได้โดยตรงโดยไม่ลดหย่อนประสิทธิภาพ ประการที่สองคือเป็นภาษาที่เรียบง่าย มี syntax ไม่มากและไม่ซับซ้อนและยุ่งยาก สามารถเรียนรู้ได้เร็ว และเมื่อเทียบ Objective-C กับภาษาเชิงวัตถุอื่นๆ ภาษา Objective-C มีความหยืดหยุ่นกว่ามาก

FinalScore Project

พูดเรื่องทฤษฎีมากแล้วเรามาลองลงมือสร้างโปรเจคเล็กเพื่อเรียนรู้การเขียนโปรแบบ OOP ไปด้วยกันดีกว่า สมมติว่าเราจะเขียนโปรแกรมที่ไว้คิดคำนวนเกรดของนักเรียน เราจะสร้างโปรเจคใหม่ขึ้นมาและตั้งชื่อให้เรียบร้อยดังรูป

( รูป 2 หน้าต่าง New Project )

ชนิดของ Type เมื่อเริ่มสร้างโปรเจคใหม่ อย่าลืมว่าต้องเลือก Foundation

เมื่อเราได้สร้างโปรเจคเสร็จเรียบร้อย ลำดับต่อไปเราจะสร้างคลาสแรกของเรากัน

เราสามารถสร้าง Class ใหม่ขึ้นมาได้โดยการกดไปที่เมนู File > New > New Files ดังรูป

( รูป 3 หน้าต่าง New File )

หรืออาจจะคลิ๊กขวาไปที่โฟลเดอร์บริเวณ Navigation ( ไอคอนสีเหลือง ) และเลือก New Files ก็ได้เหมือนกัน

( รูป 4 )

เมื่อได้เลือก New Files เรียบร้อยแล้วจะเห็นหน้าต่างให้เลือกชนิดของไฟล์ที่ต้องการสร้าง ให้เลือก Mac OS X > Cocoa > Objective-C class และกด Next ตามรูป

( รูป 5 เลือกชนิดของไฟล์ )

หลังจากกด Next แล้วต่อมาก็จะเป็นหน้าต่างถามชื่อของ Class ที่ต้องการจะสร้าง

( รูป 6 ตั้งชื่อ class )

Class Name
หลักการในการตั้งชื่อคลาสนั้นก็เหมือนกับหลักการในการตั้งให้ตัวแปร แต่ชื่อของคลาสมักจะขึ้นต้นด้วยตัวใหญ่เสมอ ถึงแม้ว่าไม่ได้มีกฎว่าห้ามใช้ตัวอักษรพิมพ์เล็กนำหน้าแต่ก็ไม่ควรใช้เพราะจะทำให้สับสนกับชื่อ Instance อย่างในโปรเจคนี้เราจะชื่อคลาสของเราว่า Student

 

จากนั้นโปรแกรมจะขึ้นหน้าต่างถามว่าจะเอาไฟล์ไปเก็บไว้ที่ไหน ก็ให้เลือกไปยังโปรเจคที่เราได้สร้างไว้ ( ปกติจะเลือกให้อัตโนมัติอยู่แล้ว )

( รูป 6 )

ให้กดไปที่ create ก็เป็นอันเสร็จ เราจะได้ไฟล์มาสองไฟล์คือ Student.h และ Student.m

( รูป 8 )

การสร้างคลาส ของภาษา Objective-C จะแบ่งออกเป็นสองส่วนด้วยกันคือ

1) interface ซึ่งส่วนที่ใช้ในการประกาศชื่อของคลาส ตัวแปรของคลาส และเมธอด ต่างๆ โดยจะใช้ header file (.h) เป็นส่วนในการเขียน

2) implementation เป็นส่วนที่เขียนการทำงานของเมธอดของคลาส ซึ่งจะเขียนในไฟล์ .m

 

@interface

การประกาศคลาสของภาษา Objective-C โดยเริ่มด้วยคำสั่ง @interface ตามมาด้วยการตั้งชื่อให้กับคลาส ถ้าคลาสสืบทอดมาจากไหนก็ต้องบอกให้คอมไพลเลอร์รู้ด้วยการใช้เครื่องหมาย : และตามด้วยชื่อของ Parent Class เมื่อบอกคอมไพลเลอร์ให้รู้แล้วหลังจากนั้นก็กำหนดข้อมูลที่จะใช้ในคลาส เราเรียกตัวแปรหรือข้อมูลของคลาสว่า instance variable และสุดท้ายก็คือกำหนดว่าคลาสนี้ทำอะไรได้บ้างหรือประกาศ method นั่นเหละครับ เมื่อทุกๆอย่างเรียบร้อยแล้วก็จบด้วยคำสั่ง @end เพื่อบอกให้คอมไพลเลอร์รู้ว่าจบการกำหนดคุณสมบัติต่างๆของคลาสแล้ว

 

รูปแบบของการประกาศคลาสของภาษา Objective-C จะมีรูปแบบดังนี้

 

@interface NewClass: Parent

method Implement

@end

 

กลับไปยังโปรเจคของเราและเปิดดูไฟล์ Student.h ซึ่งเป็นไฟล์ที่ไว้สำหรับประกาศคลาส ก็จะมีโค้ดดังนี้

 

 

จะเห็นว่า Xcode ได้เขียนโค้ดของคลาส Student ไว้คร่าวๆ คลาส Student นี้มี parent เป็น NSObject ซึ่งเป็นคลาสพื้นฐาน ของอ๊อบเจ็กทุกๆอย่าง ( root class ) ถ้าเคยเขียน OOP จากภาษาอื่นมาก่อนอาจจะสงสัยว่าทุกครั้งต้อง subclass มาจาก NSObject เสมอเลยเหรอ ? จะบอกว่า ใช่ ก็ไม่ถูกต้องเท่าไหร่นัก เพราะจริงๆไม่ต้อง subclass จากคลาสไหนเลยก็ได้ แต่การซับคลาส NSObject นี้อำนวยความสะดวกให้เราหลายๆอย่างเช่นการจองหน่วยความจำ ดังนั้นก็แทบจะทุกครั้งที่สร้างคลาสเราจะซับคลาส NSObject เสมอ และเดี๋ยวจะอธิบายเพิ่มเติมเกี่ยวกับ NSObject เพิ่มเติมในภายหลัง

 

ในตอนนี้เรามีคลาสว่างๆอยู่ เดี๋ยวเราจะลองเขียนคลาสจากเหตุการณ์ ให้ลองคิดภาพตามเหตุนะครับ

 

ปิติ : นี่เธอไปดูคะแนนสอบหรือยัง เค้าประกาศไว้ที่หน้าห้องแล้วนะ ?

มานี : ยังไม่ได้ไปดูแต่อาจารย์บอกคะแนนเราแล้ว นี่เราจดคะแนนมาด้วยเหละ

ปิติ : อ้าวเหรอ คะแนนเป็นยังบ้างอะ ?

มานี : เราได้คะแนนกลางภาค 25 และคะแนนปลายภาค 52

ปิติ : โอ้โหคะแนนเยอะมากเลย ได้เกรด 4 เลยเหรือเปล่าเนี่ย

มานี : ไม่น่าใช่นะ เพราะเราได้คะแนน 25 กับ 52 รวมกันก็เป็น 77 น่าจะได้เกรด 3 นะ

ปิติ : อีก 3 คะแนนเองก็จะได้เกรด 4 แล้วเนอะ เอายังงี้ไม๊ เดี๋ยวเราแก้คะแนนให้เธอเป็น 80

มานี : จะทำได้ยังไงละ !!!

ปิติ : ฮ่าๆนั่นสินะ

 

เมื่อนำบทสนทนา มาวิเคราะห์ถึงตัวนักเรียนว่าควรมีข้อมูลและสามารถทำอะไรได้บ้าง มาเขียนเป็นตารางก็ได้ดังตัวอย่าง

 

Class

Data

Method

นักเรียน

คะแนนสอบกลางภาค

คำนวนเกรดวิชาคณิตศาตร์

คะแนนสอบปลายภาค

บอกรายละเอียดคะแนน

จำค่าคะแนนของตัวเอง

ดูจากตารางก็จะคิดในใจได้ทันทีว่าถ้าเราเขียนคลาส Student ข้อมูลที่น่าจะต้องมี ก็คือคะแนนของการสอบวิชาคณิตศาสตร์ และต้องมี method ที่ไว้คำนวนเกรด บอกคะแนนของตัวเอง และจำค่าคะแนนของตัวเอง เมื่อเขียนออกมาเป็นโค้ดจริงๆ ก็ออกมาหน้าตาเป็นดังนี้

 

 

Instance Variable

จากบทสนทนา เราก็รู้แล้วว่าคลาส Student นี้ต้องมีตัวแปรไว้เก็บข้อมูลคะแนนของตัวเอง นั่นก็คือคะแนนสอบกลางภาค และคะแนนสอบปลายภาค ซึ่งเราสามารถประกาศตัวแปรสำหรับเก็บคะแนน ได้ภายในระหว่างเครื่องหมาย { } เท่านั้น ไม่สามารถประกาศในส่วนอื่นๆได้ ตัวแปรที่เราได้ประกาศขึ้นมาก็คือ

 

ตัวแปรหรือข้อมูลของคลาสนั้นจะต่างจากตัวแปรที่เราได้เขียนมาในบทก่อน เพราะตัวแปรที่ใช้ในคลาสจะถือว่าเป็นค่าของ instance นั้นๆเพียงตัวเดียวเท่านั้น ส่วน instance อื่นๆ หรืออ๊อบเจ็กอื่นๆ ไม่สามารถเข้าแก้ไขได้โดยตรง ถ้ายังจำบทสนทนาได้ ปิติบอกว่าจะแก้คะแนนให้กับมานี ซึ่งก็เป็นเรื่องที่ทำไม่ได้ เพราะคะแนนของมานีก็เป็นของมานี ส่วนคะแนนของปิติก็เป็นคะแนนของปิติ การไม่ให้คนอื่นแก้ไขหรือปกปิดข้อมูลของ instance ก็คือ encapsulation นั่นเหละครับ หรือเรียกอีกอย่างว่า information hiding นอกจากจะไม่ให้คนอื่นเข้าแก้ไขได้โดยง่ายแล้ว เมื่อเราต้องการดูคะแนนของมานี ก็แน่ใจได้เลยว่าเป็นของมานีไม่ใช่ของปิติแน่นอน เพราะข้อมูลของแต่ละ instance นั้นจะเป็นข้อมูลของตัวเองไม่ปะปนกับคนอื่นเลย

 

Method

เมื่อไม่สามารถเข้าแก้ไขค่าต่างของ instance ได้โดยตรง เราจึงต้องประกาศ method เพื่อให้เราส่งสารบอกให้ instance นั้นทำการเปลี่ยนแปลงค่าข้อมูลของตัวเอง เช่นเดียวกับการที่อาจารย์เดินมาบอกคะแนนมานี นั่นเหละครับมานีก็ต้องเป็นคนจำและบันทึกข้อมูลคะแนนของตัวเองไว้ อาจารย์เป็นเพียงแค่คนบอกข้อมูลเท่านั้น เราก็เลยต้องประกาศ method ไว้สำหรับให้กับคลาส Student เพื่อสามารถรับข้อมูลและแก้ไขค่าของคะแนนของตัวเองได้

 

 

จากโค้ดตัวอย่าง การประกาศ method นั้น มีส่วนประกอบหลายส่วนด้วยกันคือ

Method type
เมื่อเราประกาศเมธอดต้องระบุว่าเมธอดนี้เป็นชนิดใดชนิดหนึ่งซึ่งภาษา Objective-C ได้แบ่งชนิดของ method ออกเป็นสองชนิดด้วยกันคือ

Class Method ใช้สัญลักษณ์เครื่องหมายบวก +
Instance Method ใช้สัญลักษณ์เครื่องหมายลบ –

Class Method ชื่อก็บอกอยู่แล้วว่าเป็นของ Class เราไม่ต้องสร้าง instance สามารถเรียกใช้ได้โดยเลย ซึ่งจะมีลักษณะเหมือนกับฟังชั่นในภาษา C คลาสเมธอดส่วนมากมักจะเอาไว้ใช้สำหรับการจองหน่วยความจำเมื่อต้องการสร้าง instance เช่นเมธอด alloc เปรียบได้กับคำสั่งผลิตกล้องนั่นเหละ มันควรจะเป็นคำสั่งของคลาสหรือแบบพิมพ์เขียวที่ทำหน้าที่ผลิตกล้อง และเราจะได้เขียน class method ภายหลัง

 

ส่วน Instance Method ก็คือ method ของ instance นั่นเหละครับ จะต้องมี instance เสียก่อนถึงจะทำการเรียกใช้งานได้ เหมือนกับการกดชัดเตอร์ คงไม่ได้กดที่แบบพิมพ์เขียวใช่ไหมครับ ต้องกดที่ตัวกล้อง เมื่อดูจากโค้ดก็น่าจะเดาได้ไม่ยากว่า method ที่เราได้ประกาศไปเป็นแบบ instance method ทั้งหมดเลย เพราะใช้เครื่องหมาย – นั่นเอง

Return type
เมื่อประกาศ method เราต้องกำหนดชนิดของค่าจะส่งกลับเมื่อเมธอดนี้ทำงานเสร็จ เหมือนที่ปิติถามมานีว่าได้เกรดอะไร ? ปิติก็ต้องการคำตอบจากมานีเช่นกัน แต่จากเมธอดตัวอย่างสองเมธอด ค่าที่ส่งค่ากลับมาเป็นชนิด void ซึ่งหมายถึงไม่ต้องส่งค่ากลับ เพราะเราไม่ได้ทำการคำนวนอะไรหรือต้องการคำตอบอะไรจาก method สองอย่างนี้ ลองดูอีกสักเมธอด

เมธอด getMathGrad นี้จะค่าที่ส่งกลับมาคือ int เพราะสิ่งที่ต้องการจากเมธอดนี้ก็คือตัวเลขของเกรดนั่นเอง
Method Name
การตั้งชื่อของ method นั้นก็เหมือนกับการตั้งชื่ออื่นๆ แต่การตั้งชื่อ method นั้นนิยมตั้งชื่อตัวอักษรพิมพ์เล็ก และไม่นิยมนำหน้าด้วยเครื่องหมาย _ (Underscore)
Method Takes argument
สัญลักษณ์ : เป็นสัญลักษณ์ที่ไว้บอกว่า method นี้รับ argument
Argument type
ถ้ากำหนดให้เมธอดนี้รับพารามิเตอร์ ก็ต้องกำหนดชนิดของพารามิเตอร์ด้วย
Argument Name
ชื่อของอากิวเม้นต์ มีหลักการตั้งชื่อเช่นเดียวกันกับชื่อตัวแปร

@implement

การประกาศ @interface ที่ผ่านมาเป็นเพียงแค่การประกาศบอกว่าคลาสนี้มีอะไรบ้างเหมือนการร่างแบบ กำหนดสเปกต่างๆ หลังจากกำหนดเรียบร้อย เราก็ต้องเขียนการทำงานของเมธอดต่างๆ เพื่อให้คลาสทำงาน ซึ่งจะเขียนในส่วนของ @implement นี้นั่นเอง และรูปแบบของ syntax ในการเขียน implement ของคลาสมีรูปแบบดังนี้

@implement NewClass

{

instance variable

}

method;

@end

 

และเมื่อเปิดดูที่ไฟล์ Student.m ของเราก็จะเห็นโค้ดลักษณะประมาณนี้

ไฟล์ .m นี้เหมือนกับไฟล์ .h ที่ Xcode ได้เขียนโค้ดบางส่วนไว้ให้เรา แต่ก็เป็นเพียงแค่โครงสร้างยังไม่มีโค้ดของการทำงานใดๆของเมธอด ให้เราแก้ไขโค้ดให้เป็นดังตัวอย่าง

 

 

ความผิดพลาดที่พบได้บ่อยในการเขียน implement คือต้องไม่มีเครื่องหมาย ; ต่อท้ายเมธอด

Program

เมื่อเขียนส่วน implement เสร็จเรียบร้อยแล้ว ต่อไปเราจะเขียนส่วนสุดท้าย นั่นก็คือการทำงานของโปรแกรม ซึ่งเป็นส่วนที่เราคุ้นเคยและได้เขียนกันมาตั้งแต่บทแรกๆแล้ว การเขียนการทำงานของโปรแกรมจะเขียนในไฟล์ main.m

 

 

อธิบายโค้ดทีละส่วนที่เป็นส่วนสำคัญของโปรแกรม เริ่มจากส่วนบนสุดก่อนเลย

ถึงเราจะประกาศและเขียนการทำงานของคลาสต่างๆเรียบร้อยแล้ว แต่คอมไพลเลอร์ไม่รู้ว่าเราต้องการใช้มันหรือเปล่า ดังนั้นเราต้องบอกให้คอมไพลเลอร์รู้ว่าต้องการใช้คลาส Student นี้ด้วย โค้ดบรรทัดต่อมาที่เริ่มแปลกตาก็คือ

บรรทัดนี้หมายความว่าต้องการประการตัวแปรแบบอ๊อบเจ็กชื่อ manee อ๊อบเจ็กชนิดนี้เป็นแบบ Student และได้ให้ค่าเริ่มต้นเป็น nil ซึ่งหมายถึงยังไม่มีตัวตนหรือยังว่างอยู่ ในบทก่อนๆเราได้ใช้แต่ตัวแปรที่เป็น primitive type ซึ่งเป็นตัวแปรที่มีอยู่แล้วในภาษา Objectiv-C เช่นการประกาศ int , double แต่ถ้าเป็นการประกาศตัวแปรที่เอาไว้เก็บอ๊อบเจ็กซึ่งเป็นวัตถุที่เราสร้างขึ้นเองจะต้องมีสัญลักษณ์พิเศษเพิ่มเติมก่อนหน้าชื่อของตัวแปรคือสัญลักษณ์ * เราเรียกตัวแปรแบบนี้ด้วยศัพท์ทางเทคนิคว่า reference หรือ pointer เมื่อมีตัวแปร manee ที่เอาไว้เก็บอ๊อบเจ็ก Student แล้ว เราก็ออกคำสั่งให้คลาสนั้นสร้างวัตถุขึ้นมา

เมธอด alloc เป็น class method อาจจะงงว่า เราไม่ได้ประกาศเมธอดนี้ไว้ แล้วทำไมคลาส Student ถึงมีเมธอดนี้ ? นั่นเป็นเพราะคลาสของเราได้สืบทอดมาจากคลาส NSObject และเมธอด alloc นี้ก็มาจากคลาสนี้นี่เหละ เพราะเป็นคุณสมบัติ polymophism นั่นเอง เมื่อเราใช้เมธอดนี้ก็จะได้ instance ของ Student หลังจากที่เรามีอ๊อบเจ็กแล้วแต่ก็เอาไปใช้ยังไม่ได้ เราต้องเตรียมอ๊อบเจ็กนั้นให้พร้อมใช้งานเสียก่อนด้วยคำสั่ง

เมธอด init เป็นของ NSObject เช่นกัน แต่เป็น instance method ความหมายของบรรทัดนี้ก็คือเราได้บอกให้ manee ให้เตรียมพร้อมไว้เพื่อการใช้งานเช่นการให้ค่าเริ่มต้นสำหรับตัวแปรต่างๆภายในคลาส เพราะหลังจากได้ instance แล้วหน่วยความจำส่วนนั้นอาจจะเคยถูกใช้งานมาก่อน เราจึงต้องปัดกวาดและเตรียมค่าต่างๆให้พร้อมก่อนการใช้งาน

 

จากโค้ด 2-3 บรรทัดที่ได้อธิบายไป ถ้าเขียนแผนภาพให้เห็นการทำงานของโปรแกรม ในขั้นตอนต่างๆก็จะมีแผนภาพดังนี้

( รูป 9 )

หลังจากทุกอย่างพร้อมใช้งานแล้ว เราส่ง message บอกคะแนนให้กับ manee

โดยอันดับแรกส่ง message ชื่อ setMathMidTerm พร้อมกับค่า 25 และหลังจากนั้นเรียก setMathExamination และค่า 52

เรียกใช้เมธอด printMathDetial เพื่อดูรายละเอียดของคะแนนต่างๆ

หลังจากนั้นเราได้คำนวนหาค่าเกรดจากการเรียกเมธอด getMatGrad และพิมพ์ผลลัพธ์ออกมาทาง Console

เมธอด release นี้ก็เป็นของ NSObject เช่นกัน เมื่อเราเลิกใช้อ๊อบเจ็กตัวนั้นแล้วก็ต้องเรียก release เพื่อคืนหน่วยความจำให้กับระบบ จริงๆแล้วเมื่อปิดโปรแกรมก็จะคืนหน่วยจำให้กับระบบอยู่แล้ว แต่เมื่อเขียนโปรแกรมไปหลายๆ โปรแกรมของเราอาจจะมีการใช้หน่วยจำมากมาย ถ้าเราไม่คืนหน่วยความจำเมื่อไม่ได้ใช้อ๊อบเจ็กแล้ว โปรแกรมก็จะเกิด memory leak ฉะนั้นแล้วขอให้จำว่า เมื่อไม่มีใครใช้ต้องคืนให้ระบบ

 

ผลลัพธ์ของโปรแกรมที่เราได้เขียนไปเป็นดังนี้

Program Output
Math – MidTerm: 25 Exam: 52
Manee Grade = 3

Message

โปรแกรมที่ได้เขียนไป เราได้ประกาศคลาสและเขียนการทำงานต่างๆของเมธอดรวมถึงการเขียนส่วนหลักในการทำงานต่างๆของโปรแกรม และได้อธิบายการทำงานอย่างคร่าวๆไปบ้างแล้ว แต่ยังมีบางส่วนทีต้องอธิบายเพิ่มเติมกันยาวหน่อย นั่นก็คือการเรียกใช้ method หรือการส่ง message

Message Expression

ภาษา Objective-C นั้นเรียกการใช้ method ว่าเป็นการส่ง message โดยจะมีรูปแบบคือ

การส่ง message เหมือนกับการส่งจดหมายหาใครสักคนและบอกให้เขาทำตามที่เราต้องการ ในบางครั้งผู้รับสารก็ต้องการของบางอย่างเพื่อให้ทำงานได้เราก็ต้องส่งอากิวเม้นต์ให้กับคนรับด้วยไม่เช่นนั้นก็ทำงานผิดพลาด การส่ง message นั้นผู้รับสารนั้นอาจจะเป็น class หรือเป็น instance ก็ได้เช่น

เป็นการส่ง message หาคลาส Student เพื่อขอให้สร้าง instance ใหม่ หรือ

คำสั่งแรกเป็นการส่งสารหา manee เพื่อบอกเตรียมความพร้อมสำหรับการใช้งาน ส่วนคำสั่งที่สองเราได้ส่งข้อความบอกกับ manee พร้อมกับข้อมูลไปให้ด้วย

Message Nesting

การส่ง message นี้เราสามารถที่จะเขียนในรูปแบบ nesting หรือซ้อนกัน เช่นจากโค้ดสองบรรทัดนี้

สามารถเขียนใหม่ด้วยบรรเดียวได้เป็น

อย่างไรก็ตามถึงแม้ว่าจะเป็นโค้ดบรรดทัดเดียว แต่การทำงานก็ยังทำงาน 2 คำสั่งเหมือนเดิม โดยจะเริ่มจากคำสั่งด้านในสุดก่อน การทำงานของโค้ดจะมีลักษณะการทำงานดังภาพ

( รูป 10 )

ถ้าสังเกตุการใช้ message nesting นี้เราได้ใช้มาตั้งแต่โปรแกรมแรกโดยที่ไม่รู้ตัวแล้ว นั่นก็คือ

และการใช้ message nesting นี้ยังสามารถนำมาใช้เป็นอากิวเม้นต์ของอีก method ก็ได้ดังตัวอย่าง

Sending Message to nil

โดยปกติในภาษาอื่นๆการเรียกใช้ method ต้องมีอ๊อบเจ็กตัวนั้นเสียก่อน ไม่อย่างนั้นจะเกิดข้อผิดพลาดของโปรแกรม แต่การส่ง message ของภาษา Objective-C มีความพิเศษกว่าภาษาอื่น เราสามารถส่งหาอ๊อบเจ็กที่ไม่มีตัวตนได้ นั่นก็คือ nil ( มีค่าเป็น 0 ) เราลองแก้ไขโค้ดของโปรแกรมให้เป็นดังนี้

 

จากโปรแกรมจะเห็นว่าไม่ได้ทำการสร้างอ๊อบเจ็กขึ้นมาเลย และถ้าลองคอมไพลดูผลลัพธ์จะเห็นว่าโปรแกรมทำงานได้ แต่ไม่มีอะไรแสดงผลเลย และแม้ว่าเราจะสามารถส่ง message หา nil ได้ แต่การเขียนโค้ดเพื่อตรวจสอบว่าอ๊อบเจ็กนั้นมีอยู่จริงก่อนหรือเปล่าจะช่วยให้โค้ดของเราอ่านง่าย และดีบั๊กได้ง่ายขึ้นดังเช่นตัวอย่าง

Multiple Arguments

ในกรณีที่เราต้องการจะส่งอากิวเม้นต์ให้กับ method หลายๆค่าก็สามารถทำได้เหมือนกับภาษาอื่นๆเช่นกัน แต่ภาษา Objective-C มีความสามารถในการตั้งชื่อให้ัเมธอดที่อธิบายความหมายได้ดีกว่าภาษาอื่นๆ สมมติว่าเราต้องการจะเพิ่ม method ให้กับคลาส Student เพื่อรับค่าคะแนนสอบทั้งสองคะแนนพร้อมๆกัน ก็อาจจะเขียน method ได้ว่า

โค้ดตัวอย่าง score และ examScore เป็นชื่อของอากิวเมนต์ ส่วน andExamination: เป็นชื่อของ method ที่เพิ่มเข้ามาเพื่ออธิบายเพิ่มเติมว่าอากิวเมนต์นี้คืออะไร และเมื่อเรียกใช้งานก็จะมีลักษณะดังกล่าว

จะเห็นว่าโค้ดอ่านเข้าใจได้ง่ายมากว่าค่า 25 คือค่าคะแนนสอบกลางภาค และค่า 52 คือคะแนนสอบปลายภาค หรือเราอาจจะไม่ใช้ส่วนขยายเพิ่มเติมก็ได้ดังตัวอย่าง

เมื่อเรียกใช้งานก็จะเป็นดังนี้

แต่โค้ดจะอ่านได้เข้าใจได้ยากว่า method นี้ค่าสองค่านี้คืออะไรบ้าง ฉะนั้นผมแนะนำว่าเราควรจะเขียนชื่อเมธอดและส่วนขยายให้ครบถ้วนจะดีกว่าการเขียนโค้ดให้สั้นๆ แต่อ่านไม่เข้าใจ

 

Common Error

ถึงแม้ว่าเราจะสามารถส่ง message หา nil ได้แล้วโดยไม่เกิด error แต่การส่ง message ที่ไม่มีอยู่จริงไปยังอ๊อบเจ็ก โปรแกรมจะเกิด error ขึ้นตอนทำงาน เพราะในภาษา Objective-C การส่ง message จะเกิดขึ้นตอน runtime ไม่ได้เกิดตอน compile time เหมือนการเรียกใช้ function หรือการเรียก method ในภาษาอื่นๆเช่นภาษา C++ หรือ Java หรือจะพูดอีกอย่างก็คือเป็น dynamic ไม่ใช่ static ลองพิจารณาโค้ดตัวอย่าง

ลองคอมไพล์และดูผลลัพธ์จะเห็นโปรแกรมทำงานผิดพลาดและแสดงผลดังนี้

-[Student printEnglishDetail]: unrecognized selector sent to instance 0x10010c740
*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[Student printEnglishDetail]: unrecognized selector sent to instance 0x10010c740’
*** First throw call stack:
(
0 CoreFoundation 0x00007fff86630fc6 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff867d1d5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff866bd2ae -[NSObject doesNotRecognizeSelector:] + 190
3 CoreFoundation 0x00007fff8661de73 ___forwarding___ + 371
4 CoreFoundation 0x00007fff8661dc88 _CF_forwarding_prep_0 + 232
5 FinalScore 0x0000000100000c24 main + 148
6 FinalScore 0x0000000100000b84 start + 52
7 ??? 0x0000000000000001 0x0 + 1
)

terminate called throwing an exception

เมื่ออ่านจาก error log บรรทัดแรก

-[Student printEnglishDetail]: unrecognized selector sent to instance 0x10010c740


ก็พอจะบอกได้ว่าเกิด error ที่คำสั่ง printEnglishDetail กับ instance หมายเลข 0x10010c740 และบรรทัดต่อมา

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[Student printEnglishDetail]: unrecognized selector sent to instance 0x10010c740’

ได้บอกกับเราว่าจากข้อผิดพลาดดังกล่าวทำให้โปรแกรมทำการปิดตัวเพราะว่า Student ไม่รู้จัก printEnglishDetail

แต่เราสามารถระวังข้อผิดพลาดดังกล่าวได้เพราะปกติจะถูกตรวจสอบเจอโดยคอมไพลเลอร์ก่อน โดยบอกเป็น warning ว่า instance method ‘-printEnglishDetial’ not found (return type defaults to ‘id’) ดังรูป

( รูป 11 )

 

บางคนอาจจะรู้สึกว่า dynamic มันไม่เห็นดีเลย ต้องให้โปรแกรมทำงานก่อนถึงจะเจอผิดพลาด แต่ในบทต่อๆไปเราจะได้เห็นข้อดีและประสิทธิภาพอันทรงพลังของ Objective-C ที่ได้ออกแบบให้การส่ง message เป็นแบบ dynamic

โหลด PDF Objective-C Chapter 4


โปรแกรมที่เป็นแบบ Procedural ที่นิยมแพร่หลายมาได้แก่ Linux Kernel , Apache Server

ศูนย์วิจัย PARC ( Palo Alto Research Center ) ยังเป็นผู้เริ่มพัฒนาสิ่งต่างๆที่เป็นที่รู้จักหลายอย่าง เช่น Laser Printer , GUI , Ethernet

class ในภาษา Objective-C ถือว่าเป็น Object ด้วย

instance ก็คือ Object นั่นเหละครับ แต่เรามักใช้คำว่า Object ในความหมายแบบไม่เจาะจง

method กับ function คล้ายกัน แต่ต่างกันที่ function ทำงานได้โดยไม่ต้องมี instance เช่น NSLog ส่วน method จะทำงานได้ก็ต่อเมื่อมี instance

ภาษา Objective-C อนุญาติให้ subclass ได้เพียงแค่ class เดียว

หลังจากจบด้วยคำสั่ง @end แล้วเราสามารถที่จะประกาศ class ใหม่ในไฟล์เดียวกันต่อเลยก็ได้ แต่ไม่นิยมทำ

Argument หรือเรียกอีกอย่างว่า Parameter

9 thoughts on “Objective-C Programming Chapter 4”

  1. ผมไม่น่าไปซื้อหนังสือมาอ่านเล้ย อ่านไป งง ไป พอมาเจอบทความนี้ เข้าใจกว่าเยอะเลย
    ขอบคุณครับ

  2. เหมือนรูปในเรื่อง Implement จะผิดนะครับ

    ปล.งานเขียนดีมากครับ เข้าใจง่ายมาก ขอบคุณมากครับ

  3. ขอบคุณสำหรับความพยายามและนำความรู้มาแบ่งปันครับ อ่านแล้วเข้าใจง่าย ยกตัวอย่างให้เห็นชัดเจนดีครับ

Leave a Reply