Objective-C Programming Chapter 17 (Part2)

Core Data

 

Core Data คือเฟรมเวิร์คที่ได้ออกแบบมาเพื่อช่วยในการจัดเก็บข้อมูล ( object persistence framework)  Core Data มีความสามารถหลายอย่างมากเป็นต้นว่า ผู้ใช้งานสามารถกำหนดได้ว่าข้อมูลที่จะจัดเก็บนั้นจะใช้ XML , SQLite หรือเก็บใน iCloud แต่แนวคิดการจัดเก็บข้อมูลของ Core Data นั้นจะต่างจาก SQL พอสมควร เพราะไม่ได้มองข้อมูลในรูปแบบตาราง แต่จะมองว่าข้อมูลในรูปแบบของอ็อบเจ็ก (Managed Object) ถึงแม้ว่าตัว Core Data เองนั้นจะเป็นเพียง Framework เพื่อใช้จัดเก็บข้อมูล ซึ่งไม่ได้รวมไปถึงการออกแบบ managed object ที่จะใช้งาน แต่อย่างไรก็ตามการออกแบบ managed object นั้น ทำได้ง่ายมากใน XCode เพราะมีเครื่องมือช่วย

Core Data Stack

ก่อนที่จะไปใช้งาน API และเครื่องมือต่างๆนั้น ต้องทำความเข้าใจเกี่ยวกับโครงสร้างของ Core Data ก่อนว่าประกอบด้วยอะไรบ้าง และแต่ละส่วนมีความสัมพันธ์กันอย่างไร อย่างที่เห็นในรูปด้านล่าง Core Data ประกอบไปด้วย 4 ส่วนด้วยกันคือ Managed Object Model , Manage Object Context , Persistant Coordinator และ Data Store ส่วนประกอบทั้งหมดนี้จะเรียกว่า Core Data Stack

core_data_stack

Data Store
เป็นชั้นล่างสุดของ framework มีหน้าที่หลักคือจัดการไฟล์ที่ใช้ในการเก็บข้อมูล เช่นการอ่านหรือเขียนไฟล์ โดยทั่วๆไปแล้วแค่ระบุว่าโปรแกรมจะจัดเก็บข้อมูลแบบใดเช่น SQLite หรือ Binary จากนั้นแทบจะไม่ต้องจัดการอะไรเพ่ิมเติมในส่วนนี้เลย

Persistent Coordinator
เรียกสั้นๆว่า coordinator เป็นตัวกลางระหว่าง store และ Mange Object Context เช่นเมื่อ Manage Object Context ต้องการข้อมูล ก็จะร้องขอผ่าน coordinator ซึ่งหน้าที่ของ coordinator ก็คือจัดการว่าข้อมูลที่ถูกร้องขอมานั้นอยู่ใน store ใด จากนั้นก็จะไปอ่านข้อมูลใน store นั้น และส่งต่อข้อมูลที่ได้กลับไปให้ Mange Object Context นั่นเอง

Manage Object Model
หรือเรียกง่ายๆว่า model เป็นส่วนที่ใช้ในการกำหนดรูปแบบของอ็อบเจ็ก (Manged Object) ที่จะใช้ในการเก็บข้อมูล  รวมไปถึงความสัมพันธ์ระหว่างข้อมูลต่างๆ ถ้าดูตามรูปจะเห็นว่าโมเดลนี้ทำงานเกี่ยวข้องกับ coordinator และอย่างที่ได้อธิบายไปว่า coordinator ทำหน้าที่เป็นตัวกลาง คือเมื่อ Manage Object Context ต้องการข้อมูล ก็ต้องติดต่อกับ coordinator แต่หลังจากที่ coordinator ได้ข้อมูลมาจาก store ก็ไม่อาจจะรู้ได้ว่าข้อมูลนี้คืออะไร ดังนั้นการจะสร้าง Managed Object จากข้อมูลที่ได้มาก็ต้องอาศัย Model  นี้นั่นเอง

Manage Object Context
หน้าที่ของ context คือใช้จัดการ manged object ทั้งหมดในโปรแกรม ไม่ว่าจะเป็นการสร้าง การลบ หรือการเปลี่ยนแปลง และเมื่อถึงจุดที่ต้องการจะจัดเก็บ ก็สามารถสั่งให้ context นี้จัดเก็บข้อมูลลงฮาร์ดดิสหรือ iCloud ได้ โดยเบื้องหลังการทำงานของ context นี้ก็จะทำงานผ่าน coordinator อีกทอดหนึ่งนั่นเอง

School Project

หลังจากทีทำความเข้าใจกับ core data stack ไปเรียบร้อยแล้ว ก็ถึงเวลาที่จะลองใช้ Core Data และโปรแกรมที่จะได้เขียนกันต่อไปนี้ เป็นโปรแกรมรายชื่อนักเรียนอย่างง่าย ประกอบไปด้วยข้อมูล 2 อย่างหลักๆด้วยกันคือ Classroom ไว้เก็บรายชื่อของนักเรียนในห้องทั้งหมด ส่วน Student คือข้อมูลของของนักเรียน ถ้าหากออกแบบฐานข้อมูลแบบ relational database ก็อาจจะได้ตารางดังที่แสดงต่อไปนี้

school_table

เมื่อพิจารณาจากตารางจะเห็นว่า สองตารางนี้มีความเชื่อมโยงกัน กล่าวคือห้องเรียนหรือ Classroom มีนักเรียนได้หลายคน เราจะเรียกความสัมพันธ์ลักษณะนี้ว่าเป็นแบบ One To Many แต่ในทางตรงกันข้ามนักเรียนแต่ละคนไม่สามารถมีรายชื่ออยู่ในห้องเรียนได้มากกว่าหนึ่งห้อง ความสัมพันธ์แบบนี้เรียกว่า One To One ถ้าหากเขียนโปรแกรมใช้การเก็บข้อมูลด้วย SQL ก็จำเป็นต้องมี Database Schema เพื่ออธิบายความสัมพันธ์ อีกทั้งยังต้องเขียน SQL Query ในการดึงข้อมูลที่ต้องการ แต่การเขียนโปรแกรมด้วย Core Data นั้นจะต่างออกไป เพราะเราจะมองข้อมูลเป็นอ็อบเจ็กไม่ใช่ตาราง และที่ง่ายไปกว่านั้นคือใน XCode มีเครื่องมือช่วยในการออกแบบ Model และความสัมพันธ์ของข้อมูล

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

new_data_model

หลังจากตั้งชื่อไฟล์เรียบร้อยแล้ว ก็จะพบกับหน้าต่างที่ใช้ในการออกแบบ Model ที่เรียกว่า Model Editor ให้กดปุ่ม Add Entity ที่อยู่ด้านล่าง ซึ่งเอนทิตีนี้ (Entity) ก็คือชื่อที่ใช้อ้างอิงถึงสิ่งต่างๆ เช่น นักเรียน ห้องเรียน เป็นต้น หลังจากกดเพิ่มเอนทิตีก็จะมีหน้าตาลักษณะดังนี้

datamodel

บริเวณตรงกลางของ Model Editor คือส่วนที่ใช้กำหนด  Attributes , Relationships และ Fetched Properties ให้กับ Entity ที่ได้สร้างขึ้น ส่วนทางด้านขวามือคือ Data Model Inpector เป็นส่วนไว้กำหนดคุณสมบัติต่างๆ

Entities & Attribute
หลังจากเพิ่มเอนทิตรีแล้วสิ่งที่จะทำในลำดับแรกสุดคือแก้ไขชื่อของเอนทิตีให้เป็น Classroom เสียก่อน โดยสามารถแก้ไขได้ที่ Inspector ด้านขวามือ จากนั้นกดเพิ่ม Attributes ซึ่งแอททริบิ้วนี้คือข้อมูลที่แสดงลักษณะของเอนทิตี้ เช่น เอนทิตี้นักเรียน ก็อาจจะมีชื่อ เพศ และคะแนน เป็นต้น ซึ่งแอททริบิ้วที่จะเพิ่มเข้าไปในเอนทิตี้นี้จะคล้ายกับตาราง Classroom ที่ได้แสดงไปก่อนหน้านี้ ให้กดเพิ่มแอททริบิ้วใหม่จำนวน 2 แอททริบิ้วด้วยกัน และกำหนดชื่อของแอททริบิ้วแรกคือ name และกำหนดชนิดของแอททริบิ้วเป็น String ส่วนแอททริบิ้วที่สองกำหนดชื่อให้เป็น teacher และเป็น String เช่นเดียวกัน ภายหลังจากการประกาศเอนทิตีและกำหนดแอททริบิ้วต่างๆเรียบร้อยแล้ว ก็จะสามารถเปลี่ยนโหมดการทำงานของ Model Editor ให้เป็นโหมดรูปภาพ (Graph) ได้ด้วยการกดปุ่ม Editor Style ด้านล่างขวามือ จากนั้นจะเห็นหน้าต่างดังนี้

entity

การออกแบบ Model ของเรายังไม่เสร็จ เพราะยังเหลืออีกหนึ่ง Entity นั่นก็คือ Student ให้ทำการเพิ่มเอนทริตรี Student ซึ่งมีแอททริบิ้ว name , gender เป็น string และ score เป็น double หลังจากที่ได้เพิ่มเอนทิตรี Student , Classroom และกำหนดแอททริบิ้วเรียบร้อยแล้ว ก็จะไปสู่ขั้นตอนต่อไป

Relationship
หากเอนทิตีที่สร้างขึ้นมีเพียงแค่แอททริบิ้ว ก็ไม่ต่างอะไรกับการเก็บข้อมูลด้วยตารางเช่น SQLite ในหัวข้อที่ผ่านมา สิ่งที่จะทำให้ model สมบูรณ์เพิ่มมากขึ้นก็คือการกำหนด relationship ให้กับ manged object ความสัมพันธ์ของคลาสทั้งสองเมื่อเขียนแผนภาพออกมาก็จะได้ดังนี้

relationship_diagram

จากรูปมี relationship ด้วยกัน 2 relastionship อันดับแรกนั่นก็คือ studentList ซึ่งเป็นความสัมพันธ์จาก classroom ไปยัง student ความสัมพันธ์นี้เป็นแบบ One To Many ส่วนความสัมพันธ์ที่สอง room ก็จะเป็นความสัมพันธ์ที่ในทางตรงข้ามกันคือ One To One จาก student ไปยัง classroom นั่นเอง

ในการประกาศความสัมพันธ์ของอ็อบเจ็กจะต้องกำหนดคุณสมบัติทั้งหมด 5 อย่างด้วยกันคือ

1. Relationship name ชื่อของความสัมพันธ์
2. Destination entity เป้าหมายของความสัมพันธ์ เช่น ความสัมพันธ์ studentList เป็นความสัมพันธ์ที่ Classroom ได้สร้างขึ้นมาโดยมี destination คือ Student
3. Type คือชนิดของความสัมพันธ์ เช่น One To One (เรียกสั้นๆว่า To One) , One To Many ( To Many)
4. Inverse relationship ความสัมพันธ์ทางตรงกันข้าม โดยทั่วไปความสัมพันธ์ระหว่างอ็อบเจ็กจะมี inverse relationship เสมอ เช่น studentList ก็จะมี homeRoom เป็นความสัมพันธ์ทางตรงกันข้าม
5. Delete rule ข้อกำหนดในการลบอ็อบเจ็ก ซึ่งจะแบ่งย่อยออกเป็น 4 อย่างด้วยกันคือ

  • nullify เป็นข้อกำหนดที่ง่ายที่สุด คือถ้าลบอ็อบเจ็ก classroom ออกไปอ็อบเจ็ก student ที่เกี่ยวเนื่องจะไม่โดนลบไปด้วย ส่วนค่า classroom ที่ใช้ใน student ก็จะถูกปรับเป็น null คิดง่ายๆคือถ้ายุบห้องเรียน นักเรียนจะไม่โดนไล่ออก แล้วก็จะทำการประกาศให้นักเรียนรู้ตัวว่าไม่มีห้องเรียนแล้ว
  • no action คือไม่จัดการอะไรให้เลย นั่นหมายความว่าถ้าหากยุบห้องเรียน นักเรียนก็จะไม่โดนไล่ออก แต่นักเรียนจะยังเข้าใจว่าตัวเองมีห้องเรียน
  • cascade คือหากยุบห้องเรียน นักเรียนทั้งหมดก็จะถูกไล่ออกไปด้วย
  • deny หมายถึงว่า ก่อนจะยุบห้องเรียนได้ ต้องไล่นักเรียนออกให้หมดเสียก่อน

เมื่อทำความเข้าใจกับคุณสมบัติต่างๆแล้ว ก็ได้เวลาสร้าง relationship แรกกันนั่นก็คือ studentList ในการประกาศ relationship ให้เลือก entity ที่ต้องการจะประกาศเสียก่อน จากนั้นกดปุ่ม Add Relationship ด้านล่างขวา หรือจะกด + ในตาราง relationship ก็ได้เช่นเดียวกัน เมื่อตั้งชื่อ relationship เรียบร้อยแล้วให้กำหนด destination เป็น Student และกำหนดให้ delete rule เป็น cascade นั่นหมายความว่าถ้าลบห้องเรียน นักเรียนในห้องก็จะโดนลบไปด้วย ส่วน Type เป็น To Many ซึ่งการกำหนด delete rule และปรับคุณสมบัติต่างๆทำได้ที่ model inspector ดังรูป

model_inspector

หลังจากประกาศเสร็จเรียบร้อยแล้ว ถ้าหากเปลี่ยน Model Editor เป็นโหมดรูปภาพก็จะเห็นเส้นเชื่อมระหว่าง Classroom และ Student ลักษณะดังนี้

relationship_single

เมื่อดูจากหัวลูกศรก็จะสัญลักษณ์ >> หมายความว่าเป็นแบบ To Many พร้อมกับชี้ไปยัง Student ซึ่งหมายถึง relationship นี้เป็นความสัมพันธ์จาก Classroom ไปยัง Student นั่นเอง ถ้าทุกอย่างถูกต้องแล้วก็ลงมือสร้างความสัมพันธ์ room กันต่อได้เลย ใน relationship ที่สองนี้จะกำหนด destination ให้เป็น Classroom ส่วน Type เป็น To One พร้อมกับกำหนดให้ความสัมพันธ์นี้เป็น invert relationship ของ studentList ด้วย และสุดท้ายกำหนด delete rule ให้เป็น nulltify หมายถึงว่าถ้าลบ Student ออกจากระบบ Classroom ก็จะรู้ว่านักเรียกคนนี้ได้ไม่ได้อยู่ในห้องเรียนแล้ว

relationship_both

หลังจากกำหนดความสัมพันธ์ครบแล้ว เมื่อดูจากรูปก็จะเห็นว่าเส้นความสัมพันธ์มีลูกศรทั้งสองด้านที่แตกต่างกัน และมาถึงขั้นตอนสุดท้ายนั้นก็คือสร้างคลาสที่จะใช้งานจริงจากโมเดลที่ได้เราได้ออกแบบไว้ ในขั้นตอนนี้ให้เลือกที่เมนู Edit > Create NSMangedObjectSubclass.. จากนั้นจะมีหน้าต่างขึ้นมาให้เลือกว่าว่า ต้องการจะสร้าง subclass จาก Entity ใดบ้าง

managedCreate

หลังจากกด Next เรียบร้อย จะขึ้นหน้าต่างให้เลือกโฟเดอร์ที่ต้องการจะ Save พร้อมกับมีช่องให้เลือก Use Scalar Property for Primitive Type ซึ่งถ้าหากเลือกตรงนี้ ข้อมูลที่เป็นตัวเลขเช่น Integer จะใช้ตัวแปรแบบ int แทน NSNumber หลังจากเสร็จขั้นตอนนี้จะได้คลาสใหม่ขึ้นมาสองคลาสคือ Classroom และ Student ซึ่งจะมีโค้ดประมาณนี้

Student.h

เมื่อพิจารณาคลาส Student ที่โปรแกรมได้สร้างให้จะเห็นว่าคลาสที่ได้มาใหม่นี้เป็น subclass ของ NSMagedObject ส่วน relationship และ attributes ที่ได้กำหนดใน Model นั้น จะถูกสร้างให้เป็นเป็น property ของคลาส

Classroom.h

ส่วนคลาส Classroom ก็มีพร๊อพเพอร์ตี้ที่เกิดขึ้นจาก relationship และ attributes เช่นกัน แต่ที่พิเศษกว่านั้นคือมีเมธอดที่ใช้เพิ่มและลบ Student ออกจาก Classroom ให้เสร็จด้วย นั่นเป็นเพราะความสัมพันธ์ studentList เป็นแบบ To Many นั่นเอง

Put them togather
หลังจากที่ได้ออกแบบโมเดลเสร็จเรียบร้อยแล้ว ต่อไปก็คือการนำคลาสมาใช้ โดยในขั้นตอนแรกต้องสร้าง Core Data Stack ขึ้นมาเสียก่อน และหากกลับไปดูรูปของ Core Data Stack จะเห็นส่วนประกอบด้วยกันทั้งหมด 4 อย่างด้วยกัน data store , coordinator , context และสุดท้าย model ที่เพิ่งได้สร้างไป ซึ่งเราจะเขียนส่วนประกอบเหล่านี้ในการทำงานหลักของโปรแกรมดังนี้

main.m

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

ต่อมาคือการระบุ store ที่ต้องทำงานร่วมกับ cooridnator เมื่อดูจากพารามิเตอร์ที่ใช้ จะเห็นว่าได้กำหนดให้ store เก็บข้อมูลด้วย SQLite ซึ่งในขั้นตอนนี้ไม่ต้องสร้างไฟล์ sqlite และออกแบบฐานข้อมูล เพียงแค่กำหนดที่อยู่ของไฟล์ที่ต้องการจัดเก็บเท่านั้น ขั้นตอนสุดท้ายคือการสร้าง context เพื่อใช้งาน หลังจากจบขึ้นตอนนี้ โปรแกรมก็มีส่วนประกอบของ Core Data Stack ครบเรียบร้อยพร้อมใช้งาน

Create Managed Object
สิ่งที่จะต้องทำต่อไปคือสร้าง managed object ขึ้นมาทดสอบการใช้งาน วิธีการเขียนนั้นจะใช้คลาส NSEntityDescription เพื่อระบุเอนทิตีที่จะใช้ในการสร้างอ็อบเจ็ก เช่นตัวอย่างโค้ดต่อไปนี้

 

ตัวแปร studentEntity เป็นตัวกำหนดว่าจะใช้เอนทิตีชื่อ Student จากโมเดลที่ได้ออกแบบไว้ หลังจากนั้นจะส่งต่อให้กับคลาส NSManageObject เพื่อสร้างอ็อบเจ็กจากเอนทิตีที่กำหนด พร้อมกับกำหนด context ที่ใช้สำหรับจัดการอ็อบเจ็กนี้

 

จากโค้ดได้สร้างอ็อบเจ็ก bio ด้วยวิธีการที่ได้อธิบายไป ในขณะที่อ็อบเจ็ก mario และ mai ใช้คลาสเมธอดที่ชื่อ insertNewObjectForEnityForName: inManagedObjectContext: ไม่ว่าจะวิธีใดก็ทำงานได้เช่นกัน  ในช่วงท้ายของโค้ดจะเห็นว่าสามารถใช้เมธอด addStudentListObject เพื่อเพิ่ม Student เข้าไปใน Classroom ได้ทันที โดยที่ไม่ต้องเขียนโค้ดใดๆเลย เมื่อมีอ็อบเจ็กต่างๆแล้ว การกำหนดค่าให้กับอ็อบเจ็ก รวมไปถึงการลบหรือเพิ่มอ็อบเจ็กใหม่จะอยู่ในหน่วยความจำเท่านั้น เพราะ context ยังไม่ได้จัดเก็บลงฐานข้อมูลจริงๆ และเมื่อต้องการจะเก็บข้อมูลลงในดิสก็ให้เรียกเมธอด save

Fetch Managed Object
หลังจาก context เรียกเมธอด save ข้อมูลทุกอย่างจะถูกจัดเก็บลงฐานข้อมูล ต่อไปก็จะทดสอบเรียกข้อมูลที่ได้จัดเก็บไป ซึ่งจะใช้คลาส NSFetchRequest เพื่อกำหนดข้อมูลที่ต้องการ

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

Program 17.2 Output

Mario Maurer (M) 67
Mai Davika (F) 82

กลับไปพิจารณาโค้ดอีกครั้ง จะเห็นว่าถึงแม้ว่าจะกำหนดให้ขอข้อมูลจากเอนทิตรี Classroom แต่โปรแกรมสามารถแสดงข้อมูลนักเรียนทุกคนที่อยู่ใน bio ได้ทั้งหมด โดยอาศัยพร๊อพเพอร์ตี้ studentList ซึ่งเป็น relationship ของ Classroom และในทางกลับกัน Student ก็สามารถที่จะเข้าถึง Classroom ผ่านทาง room ได้เช่นเดียวกัน

Leave a Reply