Objective-C Programming Chapter 14 (Part 2)

Mutual Exclusion

จากปัญหา race condition เราสามารถใช้ Mutual Exclusive Access ซึ่งเป็นการกำหนดส่วนที่ให้เทรดสามารถเข้าใช้ทรัพยากรได้ทีละเทรด เราเรียกพื้นที่ตรงส่วนนี้ว่า Critical Section และการขอเข้าใช้ทรัพยากรส่วนนี้จะเรียกว่า mutex lock การทำงานของ mutual exclusive access เหมือนเราขอลูกกุญแจเพื่อเข้าไปเปิดประตู เมื่อเราเข้าไปในห้องแล้วประตูก็จะปิดอัตโนมัติไม่ให้คนอื่นเข้าไปใช้ได้นอกจากคนที่มีกุญแจ หลังจากทำงานเสร็จเราก็ต้องคืนลูกกุญแจเพื่อให้คนอื่นได้ใช้ต่อไป การใช้ mutex lock นี้สามารถทำได้โดยการใช้การใช้คลาส NSLock เราจะแก้ไขคลาส Producer โดยเพิ่มการเพิ่ม NSLock ดังนี้

Program 14.6
Producer.h

Producer.m

Continue reading Objective-C Programming Chapter 14 (Part 2)

Objective-C Programming Chapter 14 (Part 1)

Chapter 14

Concurrent Programming

การทำงานแบบ concurrent หรือการทำงานหลายๆอย่างพร้อมๆกัน ทั้งใน Mac OS X และ iOS ได้แบ่งระดับของ API ไว้เป็นหลายระดับ ซึ่งก็มีข้อดีข้อเสียและข้อจำกัดต่างๆในแต่ละดับแตกต่างกันไป ในบททนี้เราจะได้เรียนรู้ API พื้นฐานเช่น NSThread เพื่อนำไปต่อยอดกับ Grand Central Dispatch ในบทต่อไป การทำงานแบบ concurrent จะประกอบด้วยสองส่วนสำคัญคือ process และ thread

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

Thread เทรดคือหน่วยการทำงานย่อยๆที่เกิดขึ้นในโปรเซส หากโปรเซสมีเพียงเทรดเดียวจะเรียกว่า single threading  และถ้าโปรเซสมีหลายเทรดก็จะเรียกว่า multithreading โดยที่เทรดแต่ละตัวจะทำงานอิสระแยกจากกัน เนื่องจากการทำงานของ CPU สามารถทำงานได้ทีละอย่าง ดังนั้นในระบบที่มีการทำงานแบบ multithreading เวลาในทำงานของ CPU จะถูกแบ่งเป็นส่วนย่อยๆ เพื่อใช้ในการประมวลผลแต่ละเทรด การจัดสรรเวลาการทำงานของเทรดจะมี schduler ของระบบปฎิบัติการเป็นคนจัดการ เมื่อเทรดทำงานจนครบเวลาที่ schduler จัดไว้ให้ ระบบจะทำการเก็บสถานะของเทรดที่ทำงานในขณะนั้น แล้วหยุดการทำงานไว้ จากนั้นจะสลับให้อีกเทรดทำงาน ซึ่งเรียกว่า context switch และเมื่อครบตามเวลาที่กำหนด เทรดก็จะถูกสลับเปลี่ยนไปแบบนี้เรื่อยๆ เนื่องจากการสลับการทำงานระหว่างเทรดน้ันเกิดขึ้นเร็วมาก ทำให้ดูคล้ายกับว่าเทรดทำงานพร้อมๆกัน ในกรณีคอมพิวเตอร์มี CPU มากกว่าหนึ่งตัว (Multicore) ระบบปฎิบัติการอาจจะแบ่งเทรดไปทำงานในแต่ละ CPU Core โดยที่แต่ละ CPU อาจจะมีหนึ่งเทรดหรือหลายเทรดทำงานอยู่ ในระบบคอมพิวเตอร์ที่ CPU ทำงานพร้อมกันหลายๆตัวจะเรียกการทำงานแบบนี้  Parallel ซึ่งหลายคนเข้าใจผิดว่าการทำงานแบบ parallel เหมือนกับ mutithread

โปรแกรมตั้งแต่บทแรกที่เราได้เขียนกันมาเป็นการทำงานแบบ Single threading หรือทำงานเพียงหนึ่งอย่าง การทำงานแบบนี้มีข้อดีคือจัดการทุกอย่าง เป็นอย่างๆไปตั้งแต่ต้นจนจบ ทำให้การบริหารจัดการง่าย แต่ก็มีข้อเสียคือ เสียเวลาไปกับรอให้การทำงานแต่ละอย่างเสร็จสิ้นหรือเรียกว่า idle เช่น สมมติว่า มีงาน 2 งานคืองาน A กับ C โดยที่งาน A นั้นให้ผู้ใช้งานกดปุ่มจึงสามารทำงานได้ ส่วนงาน C สามารถทำได้เลย ในระบบที่เป็น single threading จะเห็นว่าเราสูญเสียเวลาไปกับการรอให้ A ทำงานจนเสร็จ ถึงจะเริ่มการทำงาน C ได้ และเราเรียกเวลาที่ไม่ได้งานอะไรนี้ว่า idle ส่วนในระบบ multithread งาน C สามารถทำได้เลยโดยที่ไม่ต้องรอ A ทำงานเสร็จ เพื่อลด idle และทำให้ CPU เกิดประสิทธิภาพสูงที่สุดเราจึงต้องแบ่งการทำงานออกเป็นหลายๆเทรด

NSThread

การสร้างเทรดตามแบบเดิมนั้นจะใช้ฟังก์ชั่นภาษา C ในการสร้าง thread หรือเรียกว่า POSIX Thread (pthread) ซึ่งเป็นคำสั่งในระดับล่าง (Low Level) และในปัจจุบันเราไม่จำเป็นต้องใช้ pthread เพราะมีคลาสที่ช่วยให้การสร้างเทรดนั้นง่ายขึ้นนั่นก็คือ NSThread การสร้างเทรดด้วย NSThread มีอยู่ด้วยกันสองวิธีแบบแรกคือสร้างอ็อบเจ็กของ NSThread ขึ้นมาใช้งาน แบบที่สองคือใช้คลาสเมธอด detachNewThreadSelector:toTarget:withObject: โปรแกรมที่เราจะได้เขียนกันเป็นโปรแกรมแรกคือโปรแกรมจำลองการ download ซึ่งมีโค้ดดังนี้ Continue reading Objective-C Programming Chapter 14 (Part 1)

Objective-C Programming Chapter 13 (Part 2)

Encoding
วิธีการที่ NSKeyedArchiver เปลี่ยนอ็อบเจ็กให้อยู่ในรูปของไบนารี่เรียกว่า encoding หรือการเข้ารหัส สิ่งที่เราต้องทำเพิ่มเติมคือเขียนเมธอด encodeWithCoder:  ซึ่งเป็นส่วนของแคทิกกอรี่ <NSCoding> และเราต้องเป็นผู้กำหนด key และข้อมูลในคลาสว่าจะถูกจัดเก็บด้วยรูปแบบใด โดยการใช้เมธอดของคลาส NSKeyedArchiver ที่เกี่ยวข้องกับการ encode เช่น

โปรแกรม ที่เราจะเขียนต่อไปนี้จะแสดงตัวอย่างการจัดเก็บอ็อบเจ็ก Product ที่เราได้เคยเขียนไป ให้สร้างโปรเจคขึ้นมาใหม่และใช้คลาส Product จากโปรแกรม 8.7 และเพิ่มแคทิกกอรี่ NSCoding ให้กับคลาส Product ซึ่งมีโค้ดดังนี้

โค้ดที่ได้เขียนไปคือทำการจัดเก็บ _name โดยใช้เมธอด encodeObject:forKey: และกำหนดค่าคีย์ให้เป็น ProductName และอ็อบเจ็กตัวท่ี่สองคือ _price จะถูกจัดเก็บด้วยเมธอด encodeFloat:forKey: ซึ่งมีค่าคีย์เป็น ProductPrice

Program 13.5

main.m

เมื่อให้โปรแกรมทำงานจะเห็นว่าโปรแกรมสามารถจัดเก็บคลาส Product ได้และไม่แสดง error เหมือนโปรแกรมทีผ่านมา

Continue reading Objective-C Programming Chapter 13 (Part 2)

Objective-C Programming Chapter 13 (Part1)

Chapter 13

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

Property list

การจัดเก็บในรูปแบบแรกที่เราจะได้เรียนรู้กันคือ property list หรือเรียกสั้นๆว่า plist การจัดเก็บอ็อบเจ็กในแบบนี้เป็นวิธีการที่นิยมใช้กับค่า setting และ preference ต่างๆของ application ทั้งใน iOS และ Mac OS เพราะเป็นวิธีที่ง่ายและรวดเร็ว เนื่องจาก plist ไม่ซับซ้อน และการจัดเก็บเป็นไฟล์ xml ความสะดวกของการใช้ plist อีกอย่างก็คือถ้าหากอ็อบเจ็กที่เราต้องการจะจัดเก็บนั้นเป็นคลาส NSNumber , NSString , NSData , NSArray หรือ NSDictionary เราสามารถจะใช้เมธอด writeToFile:atomically: ในการจัดเก็บอ็อบเจ็กได้อย่างง่ายดาย ดังเช่นตัวอย่างโปรแกรมต่อไปนี้

Program 13.1

main.m

จากโปรแกรม เราได้ประกาศดิกชันนารีซึ่งมีสมาชิกเป็นรายชื่อผลไม้ และจำนวนของรายชื่อผลไม้ จากนั้นได้เรียกเมธอด writeToFile:atomically: เพื่อเขียนไฟล์ plist เมื่อโปรแกรมเสร็จสิ้นการทำงานเราจะได้ไฟล์ plist ที่ประกอบด้วยข้อมูลของอ็อบเจ็กที่ได้จัดเก็บไป เมื่อเราเปิดไฟล์ด้วยโปรแกรม text editor ข้อมูลใน plist เป็นเพียง xml ซึ่งมีลักษณะดังนี้

เมื่อพิจารณาจากไฟล์ plist จะเห็นว่าดิกชันนารีจะถูกจัดเก็บในรูปแบบคู่ของคีย์และข้อมูลสลับกันไป เช่น <key>Count</key> และ <interger>4</interger> และในกรณีข้อมูลเป็นอ็อบเจ็กที่เป็น collection อย่างเช่น array ก็จะมีแท็ก <array> และข้อมูลภายในอาร์เรย์นั้นๆ  เมื่อพิจารณาไฟล์ plist จะพบว่าข้อจำกัดอย่างหนึ่งของการใช้ dictionary และ plist คือเราไม่สามารถใช้ key เป็นอ็อบเจ็กอื่นได้นอกจากสตริง ในกรณีที่ดิกชันนารีมีคีย์เป็นอย่างอื่น โปรแกรมจะไม่สามารถบันทึกไฟล์ได้  หลังจาก archiving แล้วเมื่อเราต้องการจะ restore ก็สามารถทำได้ด้วยการเรียกเมธอด dictinaryWithContentOfFile: หรือหากเรา archive อ็อบเจ็กที่เป็นอาร์เรย์ ก็สามารถเรียก arrayWithContentOfFile: ใช้งานได้ เราจะเขียนโปรแกรมอีกตัวเพื่อ restore อ็อบเจ็กที่ได้จัดเก็บไป

Program 13.2

main.m

Program 13.2 Output

key:Fruits obj:(
Mango,
Banana,
Apple,
Lemon
)
key:Count obj:4

จาก Output ของโปรแกรม 13.2 ที่ได้เขียนไปแสดงให้เห็นว่าโปรแกรมสามารถ restore อ็อบเจ็กจาก plist ได้อย่างถูกต้อง และในการสร้างไฟล์ plist เราไม่จำเป็นต้องเขียนโค้ดเพื่อสร้างไฟล์ plist เราสามารถสร้างได้โดยตรงจาก file template ดังที่แสดงในรูป
plist

NSKeyedArchiver

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

การเปลี่ยนอ็อบเจ็กที่ต้องการให้เป็นไบนารี่ไฟล์ทำได้ด้วยการเรียกคลาสเมธอด archiveRootObject:toFile: ดังเช่นโปรแกรมตัวอย่างต่อไปนี้

โปรแกรม 13.3 คล้ายกับโปรแกรม 13.1 แต่สิ่งที่แตกต่างกันคือ เมื่อเปิดไฟล์ข้อมูลด้วย text editor จะไม่สามารถอ่านทำความเข้าใจได้ และหลังจากที่ได้ archive อ็อบเจ็กเรียบร้อย ในการ restore อ็อบเจ็กกลับคืนมา อ่านข้อมูลเราสามารถใช้เมธอด unarchiveObjectWithFile: ดังเช่นโปรแกรมตัวอย่าง

Program 13.3

main.m

โปรแกรม 13.3 คล้ายกับโปรแกรม 13.1 แต่สิ่งที่แตกต่างกันคือ เมื่อเปิดไฟล์ข้อมูลด้วย text editor จะไม่สามารถอ่านทำความเข้าใจได้ และหลังจากที่ได้ archive อ็อบเจ็กเรียบร้อย ในการ restore อ็อบเจ็กกลับคืนมา อ่านข้อมูลเราสามารถใช้เมธอด unarchiveObjectWithFile: ดังเช่นโปรแกรมตัวอย่าง

Program 13.4

main.m

Program 13.4 Output

key:Count obj:4
key:Fruits obj:(
Mango,
Banana,
Apple,
Lemon
)

โปรแกรมที่ได้เขียนไปนั้นสามารถจัดเก็บดิกชันนารีตามที่เราต้องการได้ แต่โปรแกรมที่ได้เขียนยังมีข้อจำกัดอยู่ เพราะถ้าหากเราใช้เมธอด arvhiveRootObject:toFile: กับคลาสที่เราเขียนขึ้นมาจะเกิด error เพราะคลาส NSKeyedArchver นั้นไม่รู้จักคลาสที่เรากำหนด ดังเช่นโปรแกรมต่อไปนี้

Program Output

-[Product encodeWithCoder:]: unrecognized selector sent to instance 0x10010a650
*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[Product encodeWithCoder:]: unrecognized selector sent to instance 0x10010a650’

ข้อความ error ที่เกิดขึ้นได้บอกกับเราว่าคลาส Product ไม่มีรองรับเมธอด encodeWithCoder: สาเหตุเป็นเพราะว่าการที่คลาส NSKeyedArchvier เปลี่ยนอ็อบเจ็กให้เป็นไฟล์ จะเข้าสู่กระบวนการที่เรียกว่า Keyed Archive ซึ่งเป็นวิธีการเก็บข้อมูลโดยการใช้ key เข้ารหัสและการถอดรหัส ส่วนคลาสที่เป็นคลาสพื้นฐานอย่าง NSDictionary , NSString , NSData หรือคลาส NSArray นั้นมีเมธอดในการเข้ารหัสและถอดรหัสอยู่แล้ว เราจึงไม่ต้องเขียนเมธอดนี้ แต่สำหรับคลาสที่เขียนขึ้นเราต้องเป็นผู้ที่เขียนเมธอดในการเข้ารหัสและถอดรหัส