Objective-C Programming Chapter 9

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

Array

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

array

 

ใน Foundation Framework มีคลาสอาร์เรย์ด้วยกัน 2 คลาสคือ immutable และ mutable เช่นเดียวกับคลาสสตริงที่เราได้ลองใช้งานกันไปในบทที่แล้ว สำหรับคลาสแรกที่จะได้ลองเขียนโปรแกรมกันก็คือ NSArray เป็นคลาสแบบ immutable ไม่สามารถเปลี่ยนแปลงแก้ไขใดๆได้ ดังนั้นการกำหนดค่าให้กับอาร์เรย์ต้องทำตั้งแต่การประกาศตัวแปร

Program 9.1

โปรแกรม 9.1 แสดงตัวอย่างการใช้งาน NSArray เริ่มต้นด้วยการประกาศอ๊อบเจ็ค daysName ซึ่งเป็น NSArray การประกาศอาร์เรย์นี้ได้กำหนดค่าเริ่มต้นของอาเรย์ด้วยเมธอด initWithObjects: พร้อมกับค่าสตริงค่าแรกนั่นก็คือ Sunday หลังจากนั้นตามด้วยเครื่องหมาย , (Commar) คั่นตัวแปรเพื่อที่เราจะได้กำหนดค่าในลำดับต่อไปนั่นคือ Monday และไล่ลำดับไปเรื่อยๆจนถึง Satuday ซึ่งเป็นตัวสุดท้าย เนื่องจากเมธอด initWithObjects: สามารถกำหนดค่าเริ่มต้นได้อย่างไม่จำกัด เมธอดนี้จึงได้กำหนดให้ปิดท้ายด้วย nil เพื่อบอกให้รู้ว่าสิ้นสุดการกำหนดค่าเริ่มต้นของอาร์เรย์

อ๊อบเจ็คต่างๆที่เราได้ใส่เข้าไปจะถูกเรียงตามลำดับก่อนหลังโดยอัตโนมัติ เช่น Sunday จะอยู่ในตำแหน่งแรกสุดและตัวถัดไปคือ Monday หมายเลขของตำแหน่งต่างๆในอาร์เรย์จะเริ่มต้นที่ 0 จากโค้ดของโปรแกรมเรากำหนดค่าของ array ด้วยกันทั้งหมด 7 ค่า เพราะฉะนั้นแล้ว index ก็จะเริ่มที่ 0 ไปจนถึงตัวสุดท้ายก็คือ 6

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

Program 9.1 Output

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

ในกรณีที่เราระบุตำแหน่งเกินขอบเขตของ array เช่นโปรแกรม 9.1 ถ้าหากเปลี่ยนเงื่อนไข for loop จาก i < 7 ให้เป็น i < 8 โปรแกรมจะเกิดข้อผิดพลาดและหยุดทำงาน พร้อมกับแจ้งข้อความบอกว่าตำแหน่งที่เราได้ระบุไปเกินขอบเขตของอาร์เรย์

*** Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘*** -[__NSArrayI objectAtIndex:]: index 7 beyond bounds [0 .. 6]’

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

 

NSMutableArray

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

Program 9.2

โปรแกรมใช้เมธอด initWithObjects: เพื่อกำหนดค่าเริ่มต้นให้กับอาร์เรย์ได้เช่นกันเดียวกันกับ NSArray (NSMutableArray เป็นซับคลาสของ NSArray) หลังจากนั้นใช้เมธอด addObject: เพิ่มอ๊อบเจ็คสตริง Orange และ Banana ในตอนนี้ fruits ก็จะมีจำนวนสมาชิกด้วยกันทั้งหมด 4 อ๊อบเจ็คนั่นก็คือ Apple , Lemon , Orange และ Banana

โค้ดส่วนต่อมาบรรทัดที่ 14 คือ for loop เมื่อลูปทำงานก็จะแสดงข้อมูลต่างๆในอาเรย์ด้วย NSLog และหลังลูปทำงานจบก็ได้ลบข้อมูลในตำแหน่งที่ 1 ด้วยเมธอด removeObjectAtIndex: ดังน้ันอ๊อบเจ็คที่ถูกลบก็คือ Lemon

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

Program 9.2 Output

Apple
Lemon
Orange
Banana
———-
Apple
Orange
Banana
———-
(
Apple,
Orange,
Banana
)

จาก Output จะเห็นว่าการแสดงสมาชิกทุกตัวใน NSArray นอกจากใช้ลูปแล้ว เราสามารถใช้ NSLog แสดงค่าสมาชิกทั้งหมดได้เช่นเดียวและเขียนโค้ดเพียงบรรทัดเดียวเท่าน้ัน

Mini Project

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

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

bookController

 

เมื่อดูจากแผนภาพจะเห็นว่าเราไม่ได้ใช้ NSMutableArray เก็บข้อมูล book โดยตรง แต่เราจะประกาศคลาส BookController ที่มีสมาชิกของคลาสเป็นอาร์เรย์แทน โดยปกติเราก็สามารถที่จะเพิ่มและลบข้อมูลในอาเรย์ได้อยู่แล้ว แต่ลองนึกภาพว่าหากต้องการจะค้นหาข้อมูลใน array , โหลดข้อมูลจาก xml , และจัดการสิ่งอื่นๆทั้งหลาย การใช้คลาส NSMutableArray โดยตรงนั้นไม่สะดวกและไม่ครอบคลุมในสิ่งที่เราต้องการ ดังนั้นแล้วเราจึงต้องออกแบบให้โปรแกรมมีความยืดหยุ่นมากกว่าเดิม โดยเพิ่มคลาส BookController เพื่อมาช่วยจัดการคลาส Book อีกขั้นหนึ่ง

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

Book.h

คลาสของเรามีเพียงเมธอดที่ใช้ในการกำหนดค่าเริ่มต้นให้กับอ๊อบเจ็คเท่านั้น และในส่วนของ implementation มีโค้ดดังนี้

Book.m

โค้ด implementation ของคลาส Book คงไม่ต้องอธิบายมากเพราะเราได้เขียนคลาสกันไปค่อนข้างเยอะแล้ว สิ่งเพิ่มเติมเข้ามานอกเหนือจากที่คลาสอื่นที่เคยลองเขียนก็คือกำหนดพร๊อพเพอร์ตี้ name และ price ให้เป็นแบบ readonly คืออ่านได้อย่างเดียว ไม่สามารถแก้ไขได้ ส่วนพร๊อพเพอร์ตี้  copies กำหนดให้เป็น assign จากนั้นเราก็เขียนโปรแกรมขึ้นมาเพื่อทดสอบว่าคลาสที่เราได้เขียนไปทำงานได้ถูกต้อง

Program Output

Naruto 40 (10)
Doraemon 45 (8)

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

 BookController.h

เมื่อประกาศ class เรียบร้อยแล้วต่อไปก็เขียนโค้ดในส่วนของ implementation ทีละเมธอด

เริ่มกันที่ในส่วนของ import โปรเจคนี้เราจะนำคลาสจากบทที่แล้วกลับมาใช้ใหม่นั่นคือ Parser มินิโปรเจคใช้คลาส Parser เพื่อเป็นตัวช่วยในการอ่านข้อมูลจากไฟล์ xml และโดยปกติคลาสของ Foundation Framework จะมี class method ที่ใช้ในการสร้างอ๊อบเจ็ค คลาส NSMutableArray ก็มีเช่นกัน เช่นเมธอดต่อไปนี้

ถึงแม้ว่าเราสามารถประกาศอาร์เรย์อ๊อบเจ็คแบบโค้ดดังกล่าวได้ แต่เมื่อดูในโค้ด init เราสร้างอ๊อบเจ็ก _book โดยใช้ alloc และ init เพราะเนื่องจากว่าเมื่อสร้างอ๊อบเจ็กโดยการใช้ class method อ๊อบเจ็คที่ได้จะเป็น autorelease object จึงไม่อาจจะเดาได้ว่าระบบจะคืนหน่วยความจำเมื่อไหร่ ดังนั้นการสร้าง class member มักจะไม่ใช้คลาสเมธอดดังกล่าว และตามกฎของการสร้างอ๊อบเจ็กเมื่อใช้งานเสร็จก็ต้องคืนหน่วยความจำให้กับระบบ เราจึง override เมธอด dealloc เพื่อคืนหน่วยความจำ

เมธอด searchBook ใช้ค้นหาหนังสือที่ต้องการ เมื่อเจอหนังสือที่ต้องการก็จะส่งอ๊อบเจ็คนั้นกลับ แต่ถ้าหากไม่เจอรายชื่อหนังสือก็จะส่ง nil กลับไป โดยปกติจะใช้เมธอด isEqualToString: ในการเปรียบเทียบสตริงว่าเท่ากันหรือไม่ แต่ในเมธอดนี้เลือกใช้ compare:options: แทนการใช้ isEqualToString: เพราะต้องการการเปรียบเทียบแบบไม่แยกตัวอักษรพิมพ์ใหญ่และพิมพ์เล็กซึ่งมีความยืดหยุ่นมากกว่า

การหาจำนวนหนังสือที่เหลือในคลังสินค้าเราจะใช้เมธอด searchBook ที่ได้เขียนไปก่อนหน้า เพื่อตรวจสอบก่อนว่ามีหนังสือที่ต้องการหรือไม่ ถ้าหาหนังสือที่ต้องการเจอก็ส่งจำนวนของหนังสือที่เหลืออยู่กลับไปหากไม่เจอก็จะส่งค่า 0

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

ส่วนเมธอดสุดท้ายค่อนข้างยาว dataFromXMLFile: ประยุกต์มาจากโปรแกรมที่ 8.10 หลักการทำงานก็เหมือนกันคือ อ่านข้อมูลจากไฟล์ xml เข้ามาเก็บไว้ตัวแปรที่เป็นสตริง จากนั้นก็ส่งต่อให้กับ Parser เพื่อนำค่าจากแท๊ก xml มาสร้างอ๊อบเจ็ค Book ต่อไปเราก็จะนำอ๊อบเจ็คที่ได้สร้างไว้เข้าไปเก็บในอาเรย์ _books นั่นเอง

การเขียนคลาสทั้งสองของเราได้จบลงแล้ว สิ่งที่ต้องทำต่อไปก็คือเขียน xml ไฟล์ขึ้นมาทดสอบการใช้งาน ดังเช่น xml 2 ไฟล์นี้

comic.xml

Manga.xml

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

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

เริ่มต้นโปรแกรมด้วยการอ่านไฟล์ manga.xml เพื่อนำข้อมูลเข้ามาในระบบ จากนั้นก็เรียกใช้เมธอด list เพื่อดูรายการหนังสือ ผลลัพธ์ทางหน้าจอ console ก็จะแสดงออกมาดังนี้

Hunter 40.00 (18)
Naruto 40.00 (5)
GTO 35.00 (5)
—————

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

GTO :5
Found: Naruto 40.00 (5)

หลังจากนั้นโปรแกรมได้โหลดข้อมูลจาก comic.xml เข้ามาเพิ่มเติม ข้อมูลที่มีทั้งหมดก็ถูกปรับไปตามข้อมูลใหม่รวมถึงยอดจำนวนหนังสือ

Hunter 40.00 (18)
Naruto 40.00 (15)
GTO 35.00 (5)
Doraemon 45.00 (8)
Berserk 50.00 (5)
Pokemon 25.00 (2)
Dragonball 30.00 (4)
—————

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

โค้ดบรรทัดที่ 27-28 ลูกค้าได้ซื้อหนังสือ Berserk จำนวน 4 เล่ม และ Pokemon จำนวน 3 เล่ม แต่หนังสือ Pokemon ไม่เพียงพอต่อการจำหน่าย จึงแสดงผลดังนี้

Sold Berserk
There are not enough(Pokemon).

เมื่อขายหนังสือเรียบร้อยก็ทำการตรวจสอบรายการสินค้าที่เหลือในคลังอีกครั้ง

Hunter 40.00 (18)
Naruto 40.00 (15)
GTO 35.00 (5)
Doraemon 45.00 (8)
Berserk 50.00 (1)
Pokemon 25.00 (2)
Dragonball 30.00 (4)
—————

จะเห็นว่าจำนวนของหนังสือ Berserk ได้ลดลงไป ตามจำนวนยอดที่ขายไป และโปรแกรม MiniProject ก็ทำงานได้สมบูรณ์

 

NSArray และ NSMutableArray เป็นคลาสที่ไว้เก็บอ๊อบเจ็คเท่านั้น ไม่สามารถใช้เก็บตัวแปรที่เป็น primitive type อย่าง int , float ได้ หากต้องการจะเก็บข้อมูลตัวเลขต้องเปลี่ยนให้เป็นอ๊อบเจ็คโดยใช้คลาสที่เก็บค่าตัวเลขได้เช่น NSNumber เป็นต้น

 

Sorting Arrays

มินิโปรเจคที่เราได้เขียนไปถ้ามีปริมาณข้อมูลจำนวนมาก การดูข้อมูลต่างๆคงไม่สะดวกมากนัก เราอาจจะใช้การจัดลำดับของข้อมูลเพื่อให้เกิดความสะดวกสะบายมากยิ่งขึ้น เช่น จัดลำดับข้อมูลโดยการเรียงตามตัวอักษรหรือราคาสินค้า โชคดีที่การจัดเรียงลำดับข้อมูลใน NSMutableArray ทำได้ค่อนข้างง่ายเพราะสามารถที่จะใช้เมธอดที่ชื่อ sortUsingSelector: เป็นตัวช่วยในการเปรียบเทียบระหว่างอ๊อบเจ็คได้ เมื่อดูที่ API Reference เขียนอธิบายไว้ดังนี้

api

API ได้อธิบายไว้ว่าคือเมธอด sortUsingSelector: นี้รับพารามิเตอร์เป็น SEL (selector) ซึ่ง selector นี้เป็นเมธอดที่ใช้ในการเปรียบลำดับระหว่างสมาชิกในอาเรย์ และเมธอดนี้จะใช้กับสมาชิกทุกๆตัวพร้อมกับพารามิเตอร์ที่เป็นอ๊อบเจ็คในการเปรียบเทียบลำดับ เมธอดที่ได้กำหนดให้เป็นตัวเปรียบเทียบลำดับต้องส่งค่า NSOrderAscedngin ในกรณีที่น้อยกว่า ส่งค่า NSOrderDescending ในกรณีที่มากกว่า และส่งค่า NSOrderedSame ในกรณีที่ค่าเท่ากัน

ดังนั้นสิ่งที่เราต้องเขียนเพิ่มเติมก็คือเขียนเมธอดที่ใช้ในการเปรียบเทียบลำดับนั่นเอง ให้เพิ่มเมธอดในคลาส Book ดังนี้

จากที่ API กำหนดมา เมธอดที่ใช้เพื่อเปรียบเทียบต้องส่งค่า NSOrderedSame หรืออื่นๆ เราจึงใช้ NSComparisonResult เพราะเนื่องจาก NSOrderedSame เป็น emum type ของ NSCompareisonResult นั่นเอง นอกจากนี้ต้องรับ parameter ซึ่งจะเป็นอ๊อบเจ็คที่ใช้ในการเปรียบเทียบ จากนั้นให้เขียนโค้ดในส่วนของการทำงาน

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

และส่วน implement ก็มีโค้ดสั้นๆดังนี้

จากนั้นเราก็จะเรียกเมธอดที่ได้เขียนไปในโปรแกรมของเรา เช่นตัวอย่าง

Program Output

Berserk 50.00 (5)
Doraemon 45.00 (8)
Dragonball 30.00 (4)
GTO 35.00 (5)
Hunter 40.00 (18)
Naruto 40.00 (15)
Pokemon 25.00 (2)
—————

ถึงตรงนี้คงพอจะเริ่มเห็นประโยชน์ของความเป็นภาษา dynamic และการใช้ selector ของภาษา Objective-C มากขึ้น

เมธอดต่างๆของอาเรย์ยังมีให้ใช้อีกมากมาย ซึ่งหนังสือเล่มนี้ไม่สามารถอธิบายการใช้งานได้ครบทุกๆอย่าง ฉะนั้นที่เหลือก็คือหน้าที่ของผู้ที่อ่านที่ต้องศึกษาเพิ่มเติมด้วยตัวเอง จาก API Reference

 

Dictionary

ดิกชันนารีคือคลาสที่ใช้เก็บข้อมูลโดยอ้างอิงตาม key เปรียบเสมือนกล่องที่มีกุญแจ หากต้องการเก็บของในกล่องก็ต้องใช้กุญแจเปิดกล่อง เมื่อต้องการจะเอาของออกจากกล่องก็ต้องใช้กุญแจดอกเดิมเปิดกล่อง

dict

การเก็บข้อมูลโดยการอ้างคีย์นี้จะต้องใช้คีย์ที่ไม่ซ้ำกันแต่อนุญาติให้คีย์เป็นอ๊อบเจ็คใดๆก็ได้ และใน Foundation Framework ก็แบ่งคลาส dictionary นี้ออกเป็น 2 แบบเช่นเดียวกันกับอาร์เรย์นั่นก็คือ NSMutableDictionary และ NSDictionary จากชื่อคงจะพอเดาได้ว่า NSMutableDictionary คือคลาสที่แก้ไขได้ และคลาส NSDictionary คือแก้ไขเปลี่ยนแปลงไม่ได้

Program 9.3

Program 9.3 Output

http://www.google.com
http://www.macfeteria.com/tutorial

โปรแกรมประกาศดิกชันนารีชื่อ urlDicationary และได้กำหนดค่าเริ่มต้นให้กับอ๊อบเจ็คด้วยเมธอด initWithObjectsAndKeys: เมธอดนี้ได้รับพารามิเตอร์ที่เป็นอ๊อบเจ็คที่ต้องการจะเก็บ และตามด้วยคีย์ ดังนั้นสตริงตัวแรก @”http://www.google.com” ก็คืออ๊อบเจ็คที่ต้องการจะเก็บและมีคีย์เป็น @”google” นั่นเอง ส่วนค่าที่เหลือก็จะเป็นลักษณะแบบเดียวกันคืออ๊อบเจ็คและคีย์สลับกันไปตามลำดับและปิดท้ายด้วย nil ลักษณะเดียวกันกับ NSArray ที่เราเคยได้เขียนไปแล้ว ดิกชันนารีที่เราได้ประกาศไปจึงมีค่าเริ่มต้นตามตารางนี้

 

table1

 

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

การจัดเก็บอ๊อบเจ็คของคลาสดิกชันนารีนั้น ค่าของคีย์ไม่จำเป็นต้องเป็นคลาส NSString เสมอไปสามารถใช้คลาสใดๆเป็นคีย์ก็ได้ นอกจากนี้ยังสามารถที่จะจัดเก็บอ๊อบเจ็คที่ต่างกันไว้ได้ในดิกชันนารีเดียวกัน  ลองดูการใช้ดิกชันนารีอีกสักตัวอย่าง

Program 9.4

สร้างอ๊อบเจ๊ค myDict ด้วย class method ของ NSMutableDictionary และใช้เมธอด setObject:forKey ในการจัดเก็บอ๊อบเจ็ค โดยอ๊อบเจ็คแรกเป็นคลาส NSNumber และมีคีย์เป็น NSString ส่วนอ๊อบเจ็คที่สองนั้นตรงกันข้ามกับอ๊อบเจ็คแรกคือมีคีย์เป็น NSNumber และมีอ๊อบเจ็กที่ต้องการจัดเก็บเป็น NSString เมื่อโปรแกรมทำงานจึงแสดงผลดังนี้

Program 9.4 Output

3.141593
7

 

Fast enumeration

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

Program Output

Chiwawa
Sisawat
Parrot
Siamese fighting fish

โปรแกรมที่เราได้เขียนไปก็ทำงานได้ถูกต้อง แต่อย่างไรก็ตามวิธีนี้ไม่ใช่วิธีที่มีประสิทธิภาพ เพราะภาษา Objective-C มีวิธีที่ดีกว่านั่นคือ fast enumeration ลักษณะการทำงานของ fast enumeration มีลักษณะคล้ายกับ for loop โดยมีรูปแบบดังนี้

format

id <key> คือคีย์ที่อยู่ในดิกชันนารี ลูปจะเริ่มต้นที่คีย์ตัวแรก และทำงานไปจนกว่าจะถึงคีย์สุดท้าย จากโค้ดของโปรแกรมที่ผ่านมาหากใช้ fast enumeration ก็จะเขียนได้แบบนี้

Program 9.5

Program 9.5 Output

Chiwawa
Sisawat
Parrot
Siamese fighting fish

การใช้ fast enumeration ทำให้โค้ดมีความกระชับมากขึ้นและทำงานได้เร็วขึ้น นอกจากนี้ fast enumeration ยังใช้กับ Collection Class อื่นๆ เช่น NSArray ได้อีกด้วย

 

Set

เซ็ทคือคอลเลคชั่นคลาสที่ไม่มีการจัดเรียง ซึ่งจากต่าง NSArray ที่ใช้ลำดับ (index) ในการอ้างอิง ส่วน NSDictionary ก็ใช้ key ในการอ้างอิง คลาส Set ใน Foundation Framework ก็แบ่งออกเป็น 2 แบบเช่นเดียวกันกับอาร์เรย์และดิกชันนารี่ นั่นคือ NSSet และ NSMutableSet

set

ลองดูโค้ดโปรแกรม 9.6 แสดงตัวอย่างการใช้งาน Set

Program 9.6

Program 9.6 Output

cat
dog
bird
fish
Set1 is equal to Set3
Set1 does not contain dog
Union {(
cat,
rat,
dog,
bird,
fish
)}
Intersection {(
cat,
bird,
fish
)}
All Object (
cat,
bird,
fish
)

จากโค้ดโปรแกรม 9.6 ถ้าสังเกตจะเห็นว่าคลาสเซ็ทนั้นไม่มีเมดธอดที่ใช้ขออ๊อบเจ็คแบบเจาะจงรายตัวเหมือนอย่าง NSArray ที่มี objectAtIndex: หรือ NSDictionary ก็มี objectForKey: เมธอดเดียวที่ NSSet มีให้ในการเข้าถึงอ๊อบเจ็คก็คือ allObjects ซึ่งจะได้ข้อมูลทั้งหมดกลับมาในรูปแบบอาร์เรย์

 

IndexSet

คลาส IndexSet แบ่่งออกเป็นสองคลาสเช่นเดียวกับคลาสอื่นๆคือ NSIndexSet และ NSMutableIndexSet คลาสอินเด็กซ์เซ็ทถูกออกแบบให้เก็บข้อมูลประเภทตำแหน่ง (index) ลองพิจารณาสถาณการณ์ต่อไปนี้

indexSet

 

สมมติว่าเราต้องการจะเก็บตำแหน่งของแถวในตารางที่ผู้ใช้งานเลือกไว้ เช่น แถวที่ 1 และแถวที่ 3 , 4 วิธีการที่ง่ายที่สุดก็คือเก็บตำแหน่งที่ถูกเลือกไว้ใน Array แต่วิธีการนี้ไม่มีประสิทธิภาพมากนัก เพราะในบางสถานการณ์ เช่นตารางมี 1000 แถว ผู้ใช้เลือกแถวตั้งแต่ 100 – 250 และตำแหน่ง 312 , 389 , 588 หากเก็บข้อมูลด้วยอาเรย์จะเห็นว่าต้องเก็บค่าตำแหน่งทั้งหมดคือ 153 ค่าด้วยกัน ทางออกที่ดีกว่านั้นคือใช้คลาสที่ออกแบบให้เก็บข้อมูลประเภทตำแหน่ง (index) อย่างเช่น NSIndexSet เพราะคลาสนี้เราสามารถระบุตำแหน่งที่เป็นระยะ และตำแหน่งโดดๆได้ จากเหตุการณ์เดียวกันถ้าเปลี่ยนมาใช้ NSIndexSet เราจะใช้ข้อมูลในการระบุตำแหน่งเพียงแค่ 4 ตัวเท่านั้นคือ (100-250) , 312 , 389 และ 588 เพื่อความเข้าใจลองดูตัวโปรแกรม 9.7

Program 9.7

จากโค้ดของโปรแกรม 9.7 ได้ประกาศอาเรย์ที่เป็นรายชื่อของเดือนทั้ง 12 เดือน จากนั้นเราประกาศ indexSet ที่เก็บค่า index คือตำแหน่งที่ 2 และระยะตั้งแต่ตำแหน่งที่ 5 จนถึง 4 ตำแหน่งถัดไป ( 5 ถึง 9 )  หลังจากนั้นก็เขียนลูปเพื่อให้แสดงค่าของเดือนที่เราได้เลือกไว้ผลลัพธ์จึงได้ดังนี้

Program 9.7 Output

March
June
July
August
September

 

NSValue

ในบางครั้งเราต้องจะเก็บข้อมูลที่เป็นข้อมูลโครงสร้างพื้นฐาน เช่นในการพัฒนาโปรแแกรมด้วย iOS อาจจะจำเป็นต้องเก็บตำแหน่งพิกัดของหน้าจอ x , y หรือต้องการจะเก็บค่าของตำแหน่งปุ่มบนหน้าจอพร้อมขนาดความกว้างยาว x , y , width , height เป็นต้น โครงสร้างข้อมูลเหล่านี้มีอยู่แล้วใน Foudation Framework ซึ่งก็คือ CGPoint , CGRect แต่อย่างไรก็ตามโครงสร้างข้อมูลเหล่านี้ก็ไม่ใช่อ๊อบเจ็ค เพราะเป็นเพียง data structure ของภาษา C เท่านั้น เราจึงไม่อาจจะเก็บค่าเหล่านี้ในคลาสที่เป็น collection เช่น NSArray ได้ การแก้ปัญหาก็คือสร้างคลาสที่ใช้เก็บข้อมูลเหล่านี้ แต่เราไม่ต้องเปลืองแรงไปเรียนคลาสเหล่านี้ เพราะคลาส NSValue ได้ออกแบบให้กับปัญหาเหล่านี้ คลาส NSValue เป็น wrapper class ที่ช่วยในการเก็บ ข้อมูลของ data structure ต่างๆเหล่านี้

 

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

การใช้งานเมธอดต่างๆของแต่ละคอลเลคชั่นหนังสือเล่มนี้ไม่อาจจะอธิบายได้ทั้งหมด ดังนั้นแล้วควรจะศึกษาการใช้งานเพิ่มเติมจาก API Document ด้วยตัวเอง

ปล. โหลด PDF ไปอ่านก็ได้ ส่วน Source Code ก็ที่ github ครับ

 


  https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html

2 thoughts on “Objective-C Programming Chapter 9”

Leave a Reply