Objective-C Programming Chapter 8

หลังจากที่เราได้เริ่มมีพื้นฐานการเขียนโปรแกรมด้วยภาษา Objective-C เช่นการเรียนคลาส ประกาศเมธอด ในบทนี้เราจะก้าวเข้าสู่การใช้งานของ Cocoa Framework ซึ่งเป็นหัวใจหลักสำคัญในการเขียนโปรแกรมด้วยภาษา Objective-C แต่ ก่อนจะเริ่มส่วนที่สองของหนังสือเล่มนี้ เราควรทำความรู้จักกับเฟรมเวิร์ก (Framework) กันสักนิดว่าคืออะไร “เฟรมเวิร์ก” สำหรับ Mac OS X แล้วหมายถึงการนำเอา Library ต่างๆมารวมไว้กัน เหมือนกับการเอาหนังสือเรื่องแสง, แรงโน้มถ่วง และ โมเมนตั้ม มาแพ็ครวมกันแล้วเรียกว่าหนังสือเหล่านี้เป็นหนังสือฟิสิกส์

ถ้ายังจำกันได้ในบทแรกๆ เกี่ยวกับประวัติความเป็นมาของ Objective-C หลังจากที่ Apple ตกลงซื้อ NEXTSTEP ก็ได้นำเอาเฟรมเวิร์กของบริษัท NEXTSTEP มาใช้งานและพัฒนาใหม่กลายเป็น Cocoa ดังนั้นชื่อของคลาสต่างๆเฟรมเวิร์กส่วนมากจึงขึ้นต้นด้วย NS ซึ่งเป็นตัวย่อของ NEXTSTEP นั่นเอง นอกจากนี้แล้ว Apple ยังมีเฟรมเวิร์กอีกตัวคือ Carbon [1] ซึ่งเป็น Framework ของภาษา C แต่ได้หยุดการพัฒนาตั้งแต่ปี 2012 หลังจากออก Mac OS 10.8

สิ่งที่จะเรียนรู้ในบทนี้คือการใช้ Foundation Framework ซึ่งเป็นเฟรมเวิร์กพื้นฐาน เช่นการใช้ตัวเลข สตริง วันที่ หรือ อาเรย์ ในการเรียกการใช้งานเฟรมเวิร์กพื้นฐานนี้สามารถทำได้โดยการใช้ #import <Foundation/Foundation.h> พูดได้ว่าแทบจะทุกโปรแกรมจะต้องรวมไฟล์นี้เข้าไปด้วยเสมอ

Number

ในบทที่ผ่านมา ถ้าหากเราต้องใช้ข้อมูลแบบตัวเลข เราก็จะประกาศตัวแปรให้เป็น int , char , double เป็นต้น ตัวแปรที่เราได้ประกาศไปทั้งหมดนี้เป็นตัวแปรที่เรียกว่า Primitive Type ไม่ใช่ตัวแปรแบบ Object ถึงตรงนี้อาจจะเกิดคำถามในใจว่าแล้วทำไมต้องทำเป็นอ๊อบเจ็กให้ยุ่งยากด้วย ประการแรกก็คือเรากำลังเขียนโปรแกรมเชิงวัตถุ ประการที่สองก็คืออ๊อบเจ็กทำให้เราสามารถส่ง message หาได้ (เรียกใช้เมธอดของอ๊อบเจ็กได้นั่นเอง) ยกตัวอย่างที่พบเห็นง่ายๆเช่น ถ้าต้องการจะเปลี่ยนตัวเลขให้เป็นตัวอักษร เพื่อนำไปใช้งานอย่างอื่น เราต้องเขียนโค้ดเพื่อแปลงค่าจาก int , double , float และอื่นๆให้เป็นตัวอักษร ขึ้นมาเองซึ่งเสียเวลา อย่างที่สามก็คือคลาสอื่นๆในกลุ่มของ Foundation มีการทำงานเกี่ยวข้องกับอ๊อบเจ็ก เป็นต้นว่าถ้าหากจะเก็บตัวเลขหลายๆค่าใน NSArray เราไม่สามารถใช้ตัวแปรแบบ int , float ได้ ต้องเป็นอ๊อบเจ็กเท่านั้น คลาสแรกของ Foundation Framwork ที่จะใช้กันนั่นก็คือคลาส NSNumber ซึ่งเป็นคลาสที่เอาไว้ใช้เก็บค่าตัวเลข

Program 8.1

 

Program 8.1 Output

char 97
int 100
integer 200
float 3
long 76511

char a
integer 100
int 200
float 3.000000
long 76511

 

@autoreleasepool

ก่อนจะอธิบายโปรแกรม มาดูในส่วนโค้ดที่แปลกตากันสักหน่อยนั่นก็คือ @autoreleasepool เมื่ออ่านชื่อก็พอจะเดาได้ว่ามันก็คือ Autorelease Pool นั่นเอง โปรแกรมในบทก่อนๆที่ผ่านมาเราได้ใช้ NSAutoreleasePool กันเป็นหลักและทุกๆครั้งที่เขียนโปรแกรมก็ต้องประกาศ Autorelease Pool เสมอ ในปัจจุบันคอมไพลเลอร์รุ่นใหม่ จึงได้เพิ่มความสะดวกให้นักพัฒนาไม่ต้องเขียน NSAutoreleasePool กันให้ยุ่งยาก เพียงแต่ใช้ @autoreleasepool { } แทน NSAutoreleasePool เท่านั้นเอง

 

กลับมาต่อด้วยโค้ดของโปรแกรม 8.1 ได้แสดงการใช้งาน NSNumber อย่างคร่าวๆ โปรแกรมข้างบนเราได้ประกาศอ๊อบเจ็กทั้งหมด 5 ตัวด้วยกัน และเราก็ไดกำหนดค่าเริ่มต้นให้กับอ๊อบเจ็กแต่ละตัวต่างกันเช่น charNumber เป็นตัวอักษร ‘a’ และ floatNumber เป็นค่า 3.214 ถ้าสังเกตุบรรทัดที่ 16 กับ 17

จะเห็นว่าเป็นค่าจำนวนเต็มทั้งคู่ แต่สิ่งที่แตกต่างกันคือบรรทัด 16 นั้นจะใช้ initWithInt โดยรับค่าแบบ int ส่วนบรรทัดที่ 17 เรียกใช้ initWithInteger โดยรับค่าแบบ NSInteger แต่อย่าเพิ่งเข้าใจผิดคิดว่า NSInteger นั้นเป็นอ๊อบเจ็กเพราะเห็นว่าขึ้นต้นด้วย NS เพราะแท้จริงแล้ว NSInteger นั้นเป็นเพียง typedef ในภาษา C เท่านั้น

การใช้ NSInteger มีข้อดีคือถ้าหากเป็นระบบ 32 bits คอมไพล์เลอร์จะแปลง NSInteger ให้เป็น 32 bits Integer ส่วนคอมพิวเตอร์ 64 bits ก็จะแปลงให้เป็น 64 bits Integer แล้วเมื่อไหร่ควรจะใช้ int หรือ NSInteger ? คำตอบคือขึ้นอยู่กับความจำเป็น เช่นสมมติว่าปัจจุบันโปรแกรมเราเขียนเป็นแบบ 32 Bits ซึ่งอาจจะต้องเก็บค่าจำนวนเต็มใหญ่มากๆ เราจึงคิดเผื่อว่าวันหนึ่งเกิดย้ายระบบจาก 32 Bits ไปยัง 64 Bits โปรแกรมก็จะสามารถเก็บค่าจำนวนเต็มได้เท่ากับ 64 Bits ซึ่งทำให้ได้ใช้ประสิทธิภาพของระบบได้เต็มที่ อีกกรณีคือตัวแปรแบบ Pointer ในระบบ 32 และ 64 มีขนาดไม่เท่ากัน ดังนั้นเพื่อความมั่นใจได้ว่าเมื่อย้ายโค้ดจากระบบ 32 Bits ไปยัง 64 Bits จะไม่เกิดปัญหาขนาดของ Pointer ที่ต่างกัน อย่างไรก็ตามอีกหลายกรณีก็ไม่มีความจำเป็นที่จะใช้ NSInteger เช่นถ้าเรารู้อยู่แล้วว่าค่าของตัวแปรมีค่าในช่วงตั้งแต่ 0 – 250 การใช้ NSInteger ก็ไม่ได้เกิดประโยชน์เลย กลับกลายเป็นใช้ตัวแปรที่มีขนาดใหญ่เกินความจำเป็น

กลับมายังโค้ดของเราต่อเมื่อดูการใช้งาน NSLog จะเห็นว่าโปรแกรมได้แบ่งออกเป็นสองส่วนด้วยกัน โดยส่วนแรกจะใช้ %@ เพื่อใช้ในการแสดงอ๊อบเจ็ก แต่ถ้าต้องการจะกำหนดให้แสดงค่าในแบบที่ต้องการก็ทำได้เช่นกัน ยกตัวอย่างเช่นบรรทัด 22 และ 29

โค้ดบรรทัด 22 ใช้ %@ เป็นการแสดงค่าของอ๊อบเจ็กโดยตรงโปรแกรมจึงแสดงค่า 96 ซึ่งเป็นค่า ASCII Code ของตัวอักษร a ส่วนโค้ดบรรทัด 29 เรียก charValue เพื่อที่จะได้ค่าที่เป็นแบบ char ดังนั้นเราจึงใช้ %c เพื่อการแสดงค่า char ผลลัพธ์ที่แสดงทางหน้าจอจึงเป็นตัวอักษร a

การเปรียบเทียบค่า NSNumber ไม่สามารถทำได้โดยใช้ == เหมือนอย่าง primitive type หากต้องการจะเปรียบเทียบค่าต้องใช้เมธอด isEqualToValue: ในการเปรียบเทียบ ดังเช่นตัวอย่างโค้ดโปรแกรม 8.2

Program 8.2

 

Program 8.2 Output

90 is equal to 90

โปรแกรม 8.2 เป็นการเปรียบเทียบ NSNumber สองจำนวนซึ่งตัวแรกประกาศเป็นแบบจำนวนเต็มมีค่าเท่ากับ 90 ส่วนตัวที่สองเป็นจำนวนทศนิยม 90.0 เมื่อเปรียบเทียบจึงมีค่าเท่ากัน นอกจากนี้แล้วการประกาศ NSNumber ก็มีความแตกต่างกันเพราะ firstNumber ใช้ class method ในการประกาศ ส่วน secondNumber ใช้ instance method ปกติแล้วคลาสทุกคลาสใน Foundation ถ้ามีคลาสนั้นมี class method เพื่อใช้ในการประกาศอ๊อบเจ็ก อ๊อบเจ็กที่ได้จากจะเป็นแบบ autorelease ดังนั้นแล้วเราจึงไม่ต้องเรียก release เมื่อสิ้นสุดการใช้งาน

เมื่อพิจารณา numberWithInt ( class method ) และ initWithInt ( instance method ) ก็จะเห็นว่าชื่อของเมธอดทั้งสองมีความคล้ายกันเป็นอย่างมาก ถึงเราจะยังไม่ได้เปิดคู่มือ Reference ก็สามารถที่จะเดาได้ว่า ถ้าต้องการจะประกาศ NSNumber ที่เก็บ Unsigned Long ด้วยคลาสเมธอด ก็น่าจะมีเมธอดที่ชื่อ numberWithUnsignedLong และเมื่อเราเปิดดู Reference ก็พบว่ามีเมธอดนี้อยู่ดังรูป
image001

 

เมื่อได้ศึกษาเกี่ยวกับ Foundation Framework มากชึ้นก็จะพบว่าคลาสอื่นๆใน Foundation Framework จะมีการตั้งชื่อ instance method และ class method ที่ออกแบบเป็นลักษณะแบบนี้เช่นเดียวกัน

String

Foundation Framwork มีคลาสที่เกี่ยวข้องกับสตริงอยู่ด้วยกัน 2 คลาสหลักๆคือ NSString และ NSMutableString จริงๆแล้วโปรแกรม Hello World ที่ได้เขียนไปตั้งแต่บทแรกนั้น เราได้เริ่มใช้สตริงไปแล้ว แต่อยู่ในรูปแบบของ constance string เช่น @”Hello World” เป็นต้น การประกาศ constance string ในภาษา Objective-C มีความคล้ายคลึงกับภาษา C เพียงแค่เพิ่มสัญลักษณ์ @ นำหน้าสตริงเท่านั้นเอง ถึงจะมีรูปแบบที่คล้ายกันแต่ภาษา C สตริงเป็นเพียงกลุ่มของข้อมูลแบบ char ที่เรียงต่อเท่านั้น ตัวอักษรที่แสดงด้วย char นี้จะเรียกว่า ASCII เนื่องจาก char เก็บค่าต่างๆได้เพียง 1 byte หรือ 255 ค่า ทำให้การแสดงตัวอักษรต่างๆทำได้จำกัด การแสดงผลภาษาอื่นๆเช่นภาษาไทย ต้องเขียนโค้ดขึ้นมาเอง ส่วนสตริงใน Objective-C เก็บข้อมูลในรูปแบบ unichar ซึ่งเป็นข้อมูลตัวอักษรแบบหลายไบต์ ทำให้เพียงพอที่จะเก็บอักระของภาษาต่างๆได้มากมายเช่นภาษาไทย ภาษาจีน หรือภาษาอื่นๆ และสิ่งที่พิเศษเมื่อประกาศ constance string ในภาษา Objective-C จะเป็นการประกาศสตริงอ๊อบเจ็ก ไม่ใช่เพียงแค่กลุ่มของ unichar

 

Program 8.3

Program 8.3 Output

Hello World
Objective-C
Objective-C

 

ตัวอย่างโค้ดโปรแกรม 8.3 แสดงการใช้งานคลาส NSString เราได้ประกาศ NSString ทั้งหมด 3 อ๊อบเจ็กด้วยกัน แต่ใช้วิธีการต่างกัน โดย hello เรียกใช้ initWithString ส่วน text ประกาศแบบ constance string และตัวสุดท้าย textClassMethod เรียกใช้คลาสเมธอด stringWithString

 

Basic String methods

คลาส NSString มีเมธอดพื้นต่างๆให้ใช้งานค่อนข้างจะครบถ้วน เช่น การเปรียบเทียบสตริง การเปลี่ยนตัวเป็นพิมพ์ใหญ่ พิมพ์เล็ก การหาค่าความยาวของสตริง ดังเช่นตัวอย่าง

 

Program 8.4

 

Program 8.4 Output

stringA Hello
tringB World
stringA is equal to stringX
Ascending
HELLO
hello
stringZ 30
stringA length: 5
stringY Hello World 30
stringW HelloWorld

 

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

การแปลงข้อมูลตัวเลขเป็นตัวอักษรก็มีเมธอดให้ใช้งาน ดังเช่นโค้ดบรรทัดที่ 44 เป็นการแปลง NSNumber ให้เป็น NSString โดยเรียกใช้ stringValue เห็นได้ว่าการเปลี่ยนตัวเลขเป็นตัวหนังสือ หรือตัวหนังสือเป็นตัวเลข ทำได้ง่ายมากๆ หรือการเปลี่ยนให้เป็นพิมพ์ใหญ่หรือพิมพ์เล็กก็ทำได้อย่างสะดวก การสร้างสตริงใหม่โดยกำหนดรูปแบบ ( format string ) ก็ทำได้ไม่ยาก ดังเช่นบรรทัดที่ 51 เราได้นำ stringA StringB และ number มาใช้สร้างสตริงใหม่ และส่วนสุดท้ายบรรทัดที่ 55 เราได้สร้างสตริงใหม่จากการนำ stringA มาต่อด้วย stringB

 

Immutable & Mutable String

ตามที่ได้กล่าวไปแล้วว่าสตริงใน Objective-C นั้นจะแบ่งออกเป็น NSString (Immutable) และ NSMutableString (Mutable) คำว่า Immutable ก็คือเปลี่ยนแปลงไม่ได้ ส่วน Mutable คือเปลี่ยนแปลงค่าได้ ฉะนั้นแล้วโค้ดที่ได้เขียนไปใน 8.4 ก็เป็นการใช้ สตริงแบบ Immutable หรือเป็นสตริงแบบเปลี่ยนแปลงไม่ได้นั่นเอง เพราะว่าเราประกาศเป็นคลาส NSString ทั้งหมด แต่เมื่อย้อยกลับไปดูโค้ดบรรทัดที่

 

อาจจะเกิดข้อสงสัยว่า ก็ในเมื่อเราประกาศ NSString ซึ่งเป็น Immutable ที่แก้ไขไม่ได้ แล้วทำไม stringA ถึงได้เปลี่ยนแปลงข้อมูลจาก Hello กลายเป็นอักษรพิมพ์ใหญ่ HELLO

ความเป็นจริงแล้ว stringA ไม่ได้เกิดการเปลี่ยนข้อมูลในตัวเอง แต่สิ่งเกิดขึ้นก็เมื่อเรียกใช้ uppercaseString คือ stringA จะไปสร้างสตริงใหม่ที่เป็นตัวพิมพ์ใหญ่ แล้วส่งสตริงอ๊อบเจ็กใหม่กลับมาให้ดังรูป

 

image_09
ดังนั้นเราจึงเห็นผลลัพธ์ที่หน้าจอเป็นคำว่า HELLO นั่นเอง

ใน Foundation Framework มีคลาส NSMutableString เพื่อสร้างสตริงที่สามารถเปลี่ยนแปลงค่าในภายหลังได้ การใช้งานพื้นฐานของคลาสนี้แทบจะเหมือนกับ NSString ทุกอย่าง

 

Program 8.5

Program 8.5 Output

Hello
Objective-C
Objective-C is fun !!!
Objective-C is fun ***
ObjC is fun ***

 

 

จากโค้ดของโปรแกรม เราได้ประกาศ stringA เป็นแบบ NSMutableString และเรียกเมธอด ที่เกี่ยวข้องกับการแก้ไขสตริงเช่น setString เพื่อใช้ในการเปลี่ยนสตริงใหม่ หรือ appendString เพื่อนำสตริงใหม่มาต่อท้าย ส่วนโค้ดตั้งแต่บรรทัดที่ 20

 

 

เป็นการแทนสตริง ! ด้วย * โดยเริ่มต้นที่ 0 โดยกำหนดระยะในการค้นหาเท่ากับจำนวนตัวอักษรทั้งหมดของ stringA พูดง่ายๆก็คือเริ่มต้นตั้งแต่ตัวแรกไปถึงตัวสุดท้าย ส่วน NSLiteralSearch เป็น option ที่กำหนดว่าให้หาไปทีละตัวโดยต้องตรงกันทั้งหมดหมายความว่าตัวอักษรพิมพ์ใหญ่หรือเล็กจะแตกต่างกัน ( ยังมี option อื่นๆอีกเช่น NSCaseInsensitiveSearch เพื่อกำหนดว่าตัวพิมพ์ใหญ่หรือพิมพ์เล็กมีค่าเท่ากัน ) และสุดท้ายเรียกใช้ deleteCharactersInRange เพื่อลบตัวอักษรโดยเริ่มจากตำแหน่ง 3 โดยมีขนาดความยาว 7 ตัวอักษร

 

 

image_10

ในเมื่อ NSMutableString เปลี่ยนแปลงค่าได้และใช้งานได้เหมือนกับ NSString ทุกอย่าง งั้นเราควรจะประกาศให้เป็น NSMutableString ทั้งหมดเลยดีกว่าไหม ? คำตอบคือ “ไม่ควร” เพราะ NSString นั้นมีประสิทธิภาพดีกว่า NSMutableString และบางกรณีเช่น parameter ของเมธอดควรใช้ NSString เพื่อป้องกันการเปลี่ยนแปลงค่าของ parameter จากการทำงานภายในเมธอดโดยไม่ได้ตั้งใจ

 

Searching String

คลาส NSString และ NSMutableString ได้เตรียมเมธอดเพื่อใช้ในการค้นหาสตริง หรือการแยกสตริง (Substring) ในการค้นหาสตริงเราสามารถกำหนดรูปแบบการค้นหาได้เช่นเดียวกับการแทนที่สตริง ดังโค้ดตัวอย่างด้านล่าง

 

Program 8.6

Program 8.6 Output

String objects represent character strings in Cocoa frameworks.
Representing strings as objects allows you to use strings
wherever you use other objects

String is at index 46
String does not found
First 14 chars of text: String objects
String from index 20 : sent character strings in Cocoa frameworks.
Representing strings as objects allows you to use strings
wherever you use other objects
String from index 20-26: ects

 

การกำหนดค่าเริ่มต้นให้กับสตริงในกรณีที่สตริงยาวมากๆ เราสามารถตัดแบ่งบรรทัด ได้ดังเช่นการประกาศ text (บรรทัดที่ 10-12) จากโค้ดตัวอย่าง โปรแกรมได้แสดงการหาสตริงที่ต้องการโดยการเรียกใช้เมธอด rangeOfString

 

 

ถ้าโปรแกรมค้นหาสตริงที่เราต้องการเจอก็จะได้ NSRang กลับมาซึ่งเป็น data structure ที่ประกอบไปด้วย location และ lenght เราสามารถใช้ rang.location แสดงตำแหน่งนั้นได้ แต่ถ้าหากไม่เจอค่า location จะเป็นค่า NSNotFound

 

Display data via NSLog

คลาสของ Foundation ต่างๆ สามารถใช้ %@ ผ่าน NSLog เพื่อแสดงค่าของอ๊อบเจ็กนั้นได้ทุกๆคลาส เช่น NSNumber หรือ NSString ทั้งสองคลานี้ใช้ %@ เหมือนกัน เราก็สามารถที่จะเขียนคลาสขึ้นมาใหม่และใช้ %@ ได้เช่นเดียวกันกับคลาสใน Foundation วิธีการคือเราจะทำการ Overriding Method ที่ชื่อว่า

 

– (NSString *)description;

 

เราจะเขียนโปรแกรมขึ้นมาใหม่โดยประกาศคลาสชื่อ Product

 

Program 8.7

คลาส Product ที่เราได้ประกาศขึ้นมานี้ประกอบไปด้วย class member อยู่ด้วยกัน 2 ตัวคือ _name ที่ประกาศเป็น NSMutableString และ _price เป็น float หลังจากนั้นก็ประกาศเมธอดเพื่อใช้ในการกำหนดค่าให้กับตัวแปรของคลาส

เมื่อเสร็จสิ้นจากการประกาศคลาสแล้ว สิ่งที่ต้องทำต่อไปคือ การเขียนส่วนของ implement ซึ่งมีโค้ดดังนี้

 

 

โค้ดของเมธอด init ได้จองหน่วยความจำให้กับตัวแปรต่างๆภายในคลาส และในส่วนของเมธอด setName:andPrice ที่ใช้ในการกำหนดค่าของตัวแปร สังเกตุว่าเราใช้ NSString เพื่อป้องกันการเปลี่ยนค่าของพารามีเตอร์ที่รับเข้ามา และมาถึงส่วนที่สำคัญของโปรแกรม

 

 

โค้ดส่วนนี้เราได้ override เมธอด description โดยส่งค่าสตริงที่เรากำหนดขึ้นมาใหม่ กลับไปยังคนที่เรียกเมธอดนี้ ที่เหลือก็คือเขียนโค้ดส่วนการทำงานของโปรแกรมหลัก

 

 

เพียงเท่านี้เราก็สามารถใช้ %@ เหมือนอย่าง Foundation ได้และเมื่อทำงานโปรแกรมก็จะแสดงผลออกมาดังนี้

Program 8.7 Output

Pink Furby 2900.00
Red Furby 3000.00

 

 

Autorelease object & init

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

 

 

ในส่วนของ implementation โค้ดจะมีหน้าตาดังนี้

 

 

สิ่งที่ได้เขียนเพิ่มเข้าไปคือ initWithName: andPrice เพื่อให้คลาสสามารถสร้างอ๊อบเจ็กและกำหนดค่าเริ่มต้นได้ตั้งแต่ตอนสร้าง ทำให้คลาสของเราใช้งานได้สะดวกมากขึ้น และยิ่งไปกว่านั้นเราได้เพิ่มคลาสเมธอดเพื่อสร้าง instance ของ product ซึ่งช่วยอำนวยความสะดวกในกรณีที่ต้องการจะสร้าง autorelase object อีกด้วย ดูตัวอย่างการใช้งานในโปรแกรม 8.8

 

Program 8.8

Program 8.8 Output

Pink Furby 2900.00
Red Furby 3000.00
Green Furby 3200.00

 

อ๊อบเจ๊ก pinkFurby ใช้เมธอด setName:andPrice ดั้งเดิมที่เราได้เขียนกันไปก่อนหน้านี้ ส่วน redFurby ได้กำหนดค่าให้ตั้งแต่ตอนประกาศด้วยการเรียก initWithName:andPrice และส่วนสุดท้ายเราได้สร้าง greenFurby โดยการใช้คลาสเมธอด productWithName: แม้อ๊อบเจ๊กทั้งสามนี้ทำงานได้เหมือนกัน แต่แตกต่างกันในการเรียกเมธอดเพื่อสร้างและอ๊อบเจ็กที่ได้ก็แตกต่างกันในเรื่องของคืนหน่วยความจำแบบ autorelease

 

Read string from & Write String to file

คลาส NSString มีเมธอดที่ใช้ในการอ่านและเขียนไฟล์ อย่างง่ายอยู่แล้ว (คลาสที่เกี่ยวข้องกับการใช้ไฟล์โดยตรงจะอธิบายในบทหลังๆ) เช่น

 

 

เราสามารถใช้เมธอดเหล่านี้เพื่ออ่านและเขียนไฟล์ได้ ดังโปรแกรมตัวอย่างต่อไปนี้

Program 8.9

Program 8.9 Output

String objects represent character strings in Cocoa frameworks.
Representing strings as objects allows you to use strings
wherever you use other objects

 

การทำงานของโปรแกรม 8.9 โดยคร่าวๆ คือเราได้ประกาศสตริง และโปรแกรมเรียกใช้ฟังชั่น NSHomeDirectory() โดยจะได้ค่าที่เป็น Path ของ Home User เมื่อได้ Home User path แล้วต่อไปก็เพื่อกำหนดที่อยู่ของไฟล์ที่ต้องจะเขียนไปยัง Desktop และให้ไฟล์มีชื่อว่า program_data.txt หลังจากำหนด path เรียบร้อยก็เขียนไฟล์ด้วยคำสั่ง

 

ไฟล์ที่เขียนไปเราได้กำหนดให้ encode ด้วย UTF8 และการกำหนดให้เป็น atomically หมายถึงว่าโปรแกรมจะเขียนไฟล์ไปยังตำแหน่งไฟล์ชั่วคราวที่ระบบได้สร้างขึ้น (auxiliary file) เมื่อเขียนเสร็จเรียบร้อยถ้าไม่มีข้อผิดพลาดใดๆ โปรแกรมจะคัดลอกไปยังตำแหน่งที่เราได้ระบุไว้ แต่ในกรณีที่เกิดข้อผิดพลาดเป็นต้นว่าฮาร์ดดิสเต็มก่อนที่จะเขียนเสร็จ ไฟล์จะไม่ได้ถูกคัดลอกไปยังตำแหน่งที่กำหนด (ถ้าไฟล์อยู่แล้วจะถูกเขียนทับ) หลังจากที่โปรแกรมได้เขียนไฟล์เสร็จเรียบร้อยแล้ว โปรแกรมก็จะอ่านไฟล์ที่เพิ่งเขียนไปและแสดงข้อมูล

 

NSScaner

โปรแกรมที่ผ่านๆมาเราได้ใช้เมธอดของคลาส NSString เพื่อใช้ค้นหาตำแหน่งของข้อความที่ต้องการ แต่ก็เป็นเพียงแค่การหาตำแหน่งแบบง่ายๆ ในการหาตำแหน่งของข้อมูลแบบละเอียด Foundation Framework ได้มีคลาสที่ช่วยค้นหาข้อมูลที่ต้องการไว้ให้อยู่แล้วนั่นก็คือ NSScaner

โปรแกรมที่จะได้เขียนต่อไปคือ XML Parser อย่างง่าย เพื่อทดลองการใช้ NSScaner แต่ก่อนจะลงมือเขียนโค้ดให้สร้างโปรเจคขึ้นมาใหม่ และเมื่อสร้างโปรเจคเสร็จแล้วให้ เพิ่มไฟล์ data.txt เข้าไปยังโปรเจคของเราจากเมนู New File แล้วเลือก Empty File ดังรูป

 

image003

 

เมื่อสร้างไฟล์ใหม่เรียบร้อย แล้วให้ไปที่ Build Phases ดังรูป

 

image005

และกดที่ปุ่มด้านขวามือสุด Add Build Phase แล้วเลือก Add copy file เมื่อเสร็จแล้ว จะเห็นแถบบาร์สีเทาเพิ่มขึ้นมาว่า Copy Files ( 0 item) ให้กดที่รูปสามเหลี่ยมด้านซ้าย และเลือก + จะเจอหน้าต่างให้เลือกไฟล์ ให้เลือก data.txt แล้วตั้ง Destination ให้เป็น Resources เสร็จแล้วจะได้ดังรูป

image007

 

ที่เราได้ทำไปทั้งหมดคือ เราได้สร้างไฟล์ขึ้นมาใหม่เพื่อเป็นข้อมูลของโปรแกรม แล้วหลังจากนั้นเราก็เพิ่มขั้นตอนในการ Build ไปอีก 1 ขั้นตอน นั่นก็คือการคัดลอกไฟล์ data.txt ไปไว้ยังที่เดียวกับโปรแกรม ( excuteable file ) เพื่อความสะดวกในการเรียกใช้งาน เมื่อทำขึ้นตอนเหล่านี้เสร็จเรียบร้อยต่อไปเราจะลงมือเขียนข้อมูลในไฟล์ data.txt ดังนี้

 

จากนั้นเราก็จะสแกนสตริงที่ต้องการด้วย scanUpToString

 

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

image_1-2

หลังจากนั้นเราจะให้สแกนเนอร์หาข้อมูลส่วนที่เป็น XML Tag ด้วยคำสั่ง scanString

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

image_1-3

และสุดท้ายให้สแกนเนอร์หาส่วนปิดของ tag แต่คราวนี้เราต้องเก็บเอาค่า intoString เพราะเป็นค่าที่เราต้องการไว้ยัง ret

 

image_1-4

เมื่อเขียนส่วนเมธอดของคลาสเสร็จ เราก็จะเขียนส่วนของโปรแกรมเพื่อทดสอบการใช้งาน

 

Program 8.10

 

เริ่มแรกกำหนด file path เป็น [[NSBundle mainBundle] bundlePath] ซึ่งเป็นตำแหน่งที่โปรแกรมทำงาน (เนื่องจากเราได้ให้ XCode คัดลอกไฟล์ data.txt ไปยังตำแหน่งเดียวกับ execute file ใน Build Phase) และให้โปรแกรมได้เริ่มอ่านไฟล์

หลังจากนั้น โปรแกรมจะเริ่มเข้าสู่ while loop โดยการทำงานภายในลูปคือเรากำหนดให้เริ่มสแกนหา tag ชื่อ product ถ้าหากไม่เจอก็ให้หยุดการทำงานของ loop

 

 

เมื่อเราได้ข้อมูลภายใน tag product มาแล้ว ลำดับต่อไปคือ ทำการแยกข้อมูลย่อยภาย โดยค้นหา name และ price

 

 

เมื่อเสร็จการแยกย่อยข้อมูล เราจะเลื่อนตำแหน่งของสแกนเนอร์ไปยังจุดท้ายสุดของ tag product ที่เราได้เจอในตอนแรก ( เลข 10 คือค่าความยาวของสตริง </product> )

 

จากนั้นโปรแกรมก็จะวน loop เพื่อหา product ใหม่โดยเริ่มจากตำแหน่งล่าสุดที่ได้ค้นหาไป ผลลัพธ์ของโปรแกรมจึงเป็นดังนี้

 

 

Program 8.10 Output

Red Furby 3400
Blue Furby 2700
Green Furby 4200

 

 

บทนี้เราก็ได้เรียนรู้เกี่ยวกับหัวใจหลักของ Objective-C นั่นก็คือ Foudation Framework และการประยุกต์นำไปใช้ จะเห็นว่า คลาสต่างๆภายใน Foundation นั้นมีประสิทธิภาพสูงและมีเมธอดต่างอำนวยความสะดวกมากมาย ความสนุกของการเขียนโปรแกรมด้วย Objective-C เพิ่งจะเริ่มต้นเท่านั้นครับ ในบทหน้าเราจะยังลุยกันต่อกับ Container Class ต่างๆเช่น NSArray


[1] ถ้าได้ติดตามข่าวในช่วงปี 2010 – 2012 บริษัท Adobe มีปัญหากับ Apple มากเพราะใช้ Carbon เป็นหลัก ทำให้ต้องเขียนโปรแกรมใหม่แทบจะทั้งหมด หรือแม้กระทั่งโปรแกรมของ Apple เองอย่าง iTune ก็เขียนด้วย Carbon ซึ่งกว่าจะเปลี่ยนมาใช้ Cocoa ก็ใช้เวลาหลายปี

สำหรับแบบเป็น PDF โหลดได้ตามนี้ครับ
Objective-C Chapter 8

6 thoughts on “Objective-C Programming Chapter 8”

  1. มี code ตัวอย่างของ NSScanner มันเหมือนปนกับข้อความอยู่อะครับ

  2. Chapter 6-7 ไม่มีหรือครับ เพราะผมอ่าน chapter 5 แล้วมันก็เป็น 8 เลยอะครับ หรือว่าจริงๆ แล้วเนื้อหายังต่อเนื่องกันครับ กระผมติดตามอ่านอยู่นะครับ ขอบคุณความรู้ที่สื่อออกมาได้ดีมากๆ ครับ

    1. บทที่ 7 มีนะครับ ส่วนบทที่ 6 ยังไม่เสร็จครับ 🙂

Leave a Reply