จากที่เคยเขียนบล็อกเรื่อง Core Data ทั้ง 2 ตอน (Application Layout, Development Phase) เท่าที่ลองอ่านแล้วตั้งคำถามกับตัวเองแบบคนไม่รู้ก็ได้คำถามที่ว่าทำไมต้องใช้อันนั้น ไม่ใช้ได้ไหม หรือแล้วไอ้นี่มันมีประโยชน์อะไร มีหลายข้อเหมือนกัน สุดท้ายก็เลยคิดว่าคนที่อ่านก็น่าจะคิดและเจอปัญหาแบบเดียวกัน จากเดิมที่เปลี่ยนให้บล็อกมีแค่ 2 ตอนจบ เลยกลับมาเป็นแบบเดิมที่ตั้งใจไว้ว่าน่าจะมีตอนสรุปจบตอนที่ 3 ด้วย ซึ่งตอนนี้ก็อย่างที่ได้เกริ่นไปแล้วว่าคงจะเป็นเหมือน faq ของวิธีการพัฒนาที่ผมเขียนไปแล้วครับ
Core Data มันหน้าตาเป็นอย่างไร
อย่างที่ได้บอกในตอนแรกว่า ถ้ามองอย่างง่ายที่สุดเพื่อให้เห็นภาพ Core Data คือ Object-Relational Mapping (ORM) ชนิดหนึ่ง แต่ในเชิงเทคนิคนั้น Core Data ทำงานในลักษณะของ Object Graph นั่นคือเก็บข้อมูลคุณสมบัติของออบเจ็คที่สร้างขึ้นและบอกว่าออบเจ็คนั้นมีความสัมพันธ์กับออบเจคอื่นๆ อย่างไรบ้าง จากภาพด้านบนเป็นโครงสร้างของ Core Data ที่เรียกว่า Core Data Stack สำหรับการใช้งานนั้นถ้าลองดูตามโค้ดในตอนก่อนหน้านี้ จะเห็นได้ว่าออบเจคสำคัญออบเจคหนึ่งคือ NSManagedObjectContext (Context) จากที่บอกข้างต้นว่า Core Data ทำงานในลักษณะของ Object Graph ก็กล่าวได้ว่า NSManagedObjectContext ก็คือพื้นที่ที่นำเอา Object ทั้งหมดมาจัดเรียงไว้เพื่อให้เข้าถึงได้ง่ายขึ้น ซึ่งถือเป็นส่วนที่เราเข้าไปเกี่ยวข้องด้วยมากที่สุด เพราะ Context นั้นจะเป็นตัวกลางระหว่างแอพพลิเคชั่นและ Core Data ทำหน้าที่จัดการพฤติกรรมต่างๆ เช่น เพิ่ม เปลี่ยนแปลง หรือลบข้อมูล ของ NSManagedObject (Entity Class ที่สร้างขึ้นด้วยการ subclass มาจาก NSManagedObject) แต่ถ้าดูตามโครงสร้างด้านบนแล้ว Context ไม่ได้รู้จักกับ NSManagedObject ตรงๆ แต่สื่อสารกันผ่าน NSPersistentCoordinate ที่มีหน้าที่หาว่าข้อมูลที่ Context ต้องการนั้นเก็บไว้ที่ไหน โดยอาศัยโครงสร้างข้อมูลของ NSManagedObject ที่ได้รับมาจาก Context โดยที่ข้อมูลของ NSManagedObject นั้นจะเก็บไว้ด้วยกัน 2 ที่คือในหน่วยความจำและไฟล์บนฮาร์ดดิสก์ ซึ่งตำแหน่งนั้นก็จะได้จาก NSPersistentStore จะเป็นตัวที่ชี้ว่าตอนนี้ข้อมูลที่ต้องการนั้นตอนนี้เก็บไว้ที่ใด
การขอข้อมูลจาก Context นั้น สิ่งแรกที่ต้องทำคือการส่งเงื่อนไขของ NSManagedObject ที่ต้องการให้ Context ด้วย NSFetchRequest ต้องระบุข้อมูลที่จำเป็นอย่างน้อย 3 อย่าง ด้วยกัน
- Entity ที่ต้องการ ผ่าน
entity: - เงื่อนไขการสืบค้นข้อมูลจาก NSPredicate ผ่าน
predicate:หากต้องการทั้งหมดก็ระบุเป็นnil(ค้นหาแบบไม่มีเงื่อนไข) - วิธีการจัดเรียงข้อมูลของผลลัพธ์ที่ได้ ผ่าน
sortDescriptors:
จากนั้นจึงโยนเงื่อน (NSFetchRequest) ให้กับ Context เพื่อให้ Context เริ่มหาข้อมูลตามเงื่อนไขที่ระบุคืนมาให้เรา
NSFetchRequest *fruitRequest = [[NSFetchRequest alloc] init];
// Entity assigned
fruitRequest.entity = [NSEntityDescription entityForName:@"Fruits"
inManagedObjectContext:self.managedObjectContext];
// Predicate: nil to fetch all data
fruitRequest.predicate = nil;
// Sort Condition
NSSortDescriptor *sortByPrice = [[NSSortDescriptor alloc] initWithKey:@"pricePerKilogram"
ascending:NO];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"name"
ascending:YES];
fruitRequest.sortDescriptors = [NSArray arrayWithObjects:sortByPrice, sortByName, nil];
NSError *error = nil;
// Fetch data from Context
Fruits fruit = [self.managedObjectContext executeFetchRequest:request
error:&error];
ถ้า Core Data มันเป็นฐานข้อมูลแบบที่ว่าจริง แล้วถ้าอยากทำให้มันสัมพันธ์กันจะต้องทำยังไง
fig.2: วิธีการสร้างความสัมพันธ์ภายใน Core Data
ในกรณีที่มี Entity Class มากกว่า 2 แล้วต้องการสร้างความสัมพันธ์ระหว่าง Entity Class ทั้ง 2 นั้น โดยระบุชื่อ Relationship ปลายทางที่ต้องการชี้ไป Entity Class ที่ต้องการในช่อง Destination ส่วน Inverse นั้นคือ Relationship ของคลาสที่ชี้ไป เพื่อให้คลาสนั้นค้นข้อมูลคืนกลับมายัง Entity Class นี้ได้ ซึ่งประเภทความสัมพันธ์ก็เช่นเดียวกันกับประเภทความสัมพันธ์ของข้อมูลทั่วไป เช่น 1-1, 1-n และ n-m
แล้วสคริปต์ SQL ล่ะ ?
ไม่จำเป็นต้องมี SQL สคริปต์ที่เขียน Hard Code ลงไปแบบนั้น แต่ใช้การสร้างเงื่อนไขผ่าน NSPredicate หลังจากนั้น เงื่อนไขต่างๆ ที่ระบุไปใน NSPredicate จะถูกแปลงเป็นคำสั่ง SQL และถ้าอยากเห็นคำสั่ง SQL ที่แปลงออกมาจาก NSPredicate ก็ใส่ -com.apple.CoreData.SQLDebug 1 เพิ่มเข้าไปเป็น argument หนึ่งในการทำงาน โดยกำหนดที่ กดปุ่ม command + < เพื่อแก้ไข Scheme แล้วใส่ค่าลงไปตามรูป
fig.3: การกำหนดค่าเพื่อแสดงคำสั่ง SQL ที่ debug console
ทำไมต้องใช้ NSFetchedResultsController ถ้าไม่ใช้ได้หรือเปล่า ดูมันยุ่งยากมาก
ต้องบอกว่าถ้าไม่ใช้ NSFetchedResultsController ชีวิตจะดูยุ่งยากกว่านี้มาก 3-4 เท่าตัว เนื่องจาก NSFetchedResultsController นั้นจะช่วยลดภาระการทำงานต่างๆ ลงไป เช่น การดึงข้อมูลออกมาจากตารางให้อัตโนมัติเพื่อลดภาระการทำงานส่วนการเชื่อมต่อกับฐานข้อมูล ลดจำนวนโค้ดที่ต้องใช้ ซึ่งการใช้งานจริงแล้วส่วนใหญ่จะใช้งานผ่าน NSFetchedResultController มากกว่า ดังนั้นผมเลยข้ามส่วนการใช้งานแบบปกติออกไปก่อน
ทำไมถึงไม่ใช้ Interface Builder วาด UI แล้ววาดเองผ่านโค้ดแบบนี้กับใช้ Interface Builder มันมีข้อดีข้อเสียต่างกันยังไง
ส่วนตัวแล้วไม่คิดว่ามันต่างกันครับ แต่ในแง่การใช้งานจริงนั้น การใช้ Interface Builder (IB) สร้าง UI คือการสร้างชุดคำสั่งสำหรับวาด UI ในรูปแบบ XML ดังนั้นโค้ดในตัวโปรแกรมก็จะลดลง ข้อเสียที่เกิดขึ้นคือ UI ที่ได้จาก IB จะเป็นแบบ static ซึ่งโปรเจคที่ยกตัวอย่างขึ้นมาก่อนหน้านี้ไม่จำเป็นต้องมีลักษณะจำเพาะมาก เช่น ไม่จำเป็นต้อง customize UITableViewCell เอง ก็เหมาะสมกับการใช้ IB วาดหน้า UI
สำหรับการทำงานที่ต้องการความจำเพาะเจาะจงมากกว่านั้น เช่น UI ของ Twitter for iPhone, Facebook for iPhone หรือ Path ก็จะใช้การวาด UI ด้วยโค้ด
แต่สุดท้ายแล้วไม่ว่าจะวาด UI ด้วย IB หรือโค้ด ก็ขึ้นอยู่กับความต้องถนัดของนักพัฒนาแต่ละคนว่าจะถนัดการสร้างด้วยวิธีไหนมากกว่ากัน
