Objective-C Programming Chapter 16 (Part1)

Chapter 16

LLVM & ARC

 

ที่ผ่านมาได้พูดถึง LLVM (Low Level Virtual Machine) อยู่หลายครั้ง ว่าเป็นส่วนที่สำคัญในการพัฒนาโปรแกรม แต่ยังไม่ได้อธิบายความสำคัญของ LLVM ซึ่งเป็นหัวใจของส่วนขยายความสามารถใหม่ๆในภาษา Objective-C อย่าง Automatic Reference Counting และ Block ถึงแม้ว่าชื่อมันจะบอกว่าเป็น low level virtual machine แต่ LLVM กลับเป็น library และชุดเครื่องมือ (toolschain) ที่ช่วยในการพัฒนาคอมไพลเลอร์ เพื่อที่จะให้เข้าใจที่มาที่ไปของ LLVM มากขึ้นจะขออธิบายการทำงานของคอมไพลเลอร์เบื้องต้นสักนิดหนึ่งก่อน

compiler_arch

 

ในอดีตการออกแบบคอมไพลเลอร์จะประกอบด้วยส่วนสำคัญใหญ่ๆ 3 ส่วนคือ ส่วนที่เกี่ยวข้องกับโครงสร้างของภาษา ที่เรียกว่า front end ในส่วนนี้จะมีกระบวนการปลีกย่อยอีกเช่น Parsing , Scanning , Semantic Analysis หน้าที่หลักๆของส่วนนี้คือทำหน้าที่วิเคราะห์และตรวจสอบว่าโค้ดที่เขียนมานั้นมีความถูกต้องตามข้อบังคับต่างๆของภาษา ซึ่งถ้าหากโค้ดที่เขียนมีความถูกต้องเรียบร้อยแล้ว คอมไพลเลอร์จะสร้าง Intermediet Representation (IR) ซึ่งเป็นชุดคำสั่งระดับล่าง ที่เกือบจะเท่า machine code และส่งต่อไปยัง Optimizer ซึ่งเป็นส่วนที่สอง ในขั้นตอนนี้คอมไพลเลอร์จะปรับปรุงประสิทธิภาพของ IR เช่นการตัดโค้ดที่ไม่จำเป็นออก หรือเปลี่ยนลำดับของคำสั่งเพื่อให้ทำงานได้เร็วขึ้น จากนั้นก็จะส่งต่อไปยังส่วนสุดท้ายคือ back end ซึ่งจะทำหน้าที่แปลง IR ให้กลายเป็น machine code ที่พร้อมจะทำงาน
ข้อจำกัดของการออกแบบคอมไพลเลอร์ลักษณะนี้คือไม่สามารถใช้ได้กับคอมพิวเตอร์ทุกๆสถาปัตยกรรมได้ เช่น คอมพิวเตอร์ในระบบอิเล็กทรอนิกขนาดเล็กอาจจะใช้ ARM ส่วนคอมพิวเตอร์ตั้งโต๊ะมีสถาปัตกรรมแบบ X86 หรือคอมพิวเตอร์สมถรรภาพสูงๆใช้ Itanium เป็นต้น และด้วยสถาปัตกรรมที่แตกต่างกัน ชุดคำสั่งของ CPU ก็ไม่เหมือนกัน หากต้องการจะให้คอมไพลเลอร์ทำงานได้กับสถาปัตยกรรมใด ก็ต้องเขียนคอมไพลเลอร์ขึ้นมาใหม่ ถ้าสมมติหากต้องการจะเขียนคอมไพลเลอร์เพื่อให้รองรับภาษา C , BASIC , Objective-C และสามารถทำงานกับทุกๆสถาปัตยกรรม ARM , X86 นั่นหมายถึงว่าต้องเขียนคอมไพลเลอร์ทั้งหมดเป็นจำนวนถึง 3 x 2 = 6 ตัว เลยทีเดียว หรือถ้ากำหนดให้ n เป็นจำนวนภาษา และ m เป็นจำนวนของสถาปัตยกรรมก็จะเห็นว่าจำนวนคอมไพลเลอร์นั้นมีทั้งสิ้น n x m ซึ่งเป็นจำนวนที่เยอะมากดังที่ได้แสดงในรูปต่อไปนี้

compiler_graph

ปัญหาดังกล่าวได้ถูกแก้ไขมาตั้งหลายสิบปีแล้ว ด้วยการออกแบบคอมไพลเลอรให้แบ่งออกเป็น 3 ส่วนคือ front end , back end  และส่วนสุดท้ายคือ optimizer ซึ่งเป็นตัวกลางร่วมกัน ดังเช่นที่แสดงในภาพด้านล่าง

compiler_arch2

การออกแบบลักษณะนี้มีข้อดีมากมาย เพราะเมื่อมีภาษาใหม่ๆเกิดขึ้นมา ก็เพียงแค่เขียนส่วนที่เป็น font end เพิ่มเข้าไปใหม่ หรือหากมีสถาปัตยกรรมใหม่ก็เพิ่มส่วนทีเป็น back end เข้าไปเท่านั้นเอง และยังเป็นผลดีโดยตรงต่อ Open Source ทั้งหลาย  เนื่องจากโครงการต่างๆนั้นพัฒนาด้วยภาษาที่แตกต่างกัน ทำให้สามารถแปลงโปรเจคไปทำงานในคอมพิวเตอร์ระบบต่างๆได้ง่ายมากขึ้น แม้ว่าการออกแบบลักษณะนี้มีมานานแล้ว และน่าจะช่วยแก้ปัญหาไปได้ แต่ในความเป็นจริงกลับไม่เป็นเช่นนั้น เพราะ Compiler ที่เป็น Open Source หลายๆโครงการไม่ได้มีส่วนที่ใช้โค้ดร่วมกัน หรือใช้ร่วมกันน้อยมาก อย่าง ruby กับ python ก็ต่างแยกกันทำทั้ง parser, scaner และส่วนอื่นๆไม่ได้มีส่วนหนึ่งส่วนใดที่ใช้ร่วมกันเลย อย่างไรก็ตามยังมีตัวอย่าง คอมไพลเลอร์ที่มีการทำงานที่ใกล้เคียงกับการออกแบบลักษณะนี้อยู่บ้าง นั่นก็คือ .NET และ Java คอมไพลเลอร์ทั้งสองใช้เทคนิคที่เรียกว่า JIT (Just In Time) ซึ่งจะคอมไพล์โค้ดของโปรแกรมให้เป็นคำสั่งพิเศษที่จำลองภาษาเครื่อง หรือที่เรียกว่า bytecode หรือ virtual machine instruction เสียก่อน เมื่อโปรแกรมทำงานจะทำงานผ่านตัวแปรคำสั่งที่เรียกว่า Virtual Machine ซึ่งจะเปลี่ยนคำสั่งพิเศษเหล่านี้ให้เป็น machine code ที่คอมพิวเตอร์ใช้งานจริงอีกที ข้อเสียที่ใหญ่ที่สุดของคอมไพลเลอร์แบบนี้คือการทำงานทุกอย่างต้องผ่าน Virtual Machine ทำให้ประสิทธิภาพด้อยกว่าการคอมไพล์ให้เป็น machine code มาตั้งแต่แรก ตัวอย่างที่สองคือ GCC ซึ่งเป็นคอมไพลเลอร์ที่มี font end ของหลายภาษา และยังมี back end อีกมากมาย แต่ปัญหาหลักของ GCC คือมีโครงสร้างของที่ซับซ้อน เพราะออกแบบไว้นานมาก รวมไปถึงการไม่รองรับเทคโนโลยีใหม่ๆอย่างเช่น การคอมไพล์แบบ JIT  และด้วยจุดนี้เองทำให้ Chris Latter ได้สร้างโปรเจค LLVM ขึ้นมา

llvm_arch

 

โครงสร้างของ LLVM ถูกออกแบบมาให้ทำหน้าที่เป็น back end และ optimizer และในส่วนของ font end ก็ยังทำหน้าที่เดิม นั่นตรวจสอบความถูกต้องของภาษาและแปลงโค้ดให้เป็น IR (llvm IR) จากนั้นก็จะถูกส่งต่อไปยัง llvm optimizer และในส่วนสุดท้ายคือ llvm back end เพื่อสร้าง machine code ตามลำดับ

llvm-gcc , CLang & Apple LLVM

ในช่วงเริ่มต้นของโปรเจค LLVM นั้นมีจุดประสงค์เพื่อเป็น back end อย่างเดียว ไม่มีส่วนของ font end ดังนั้นจึงได้ใช้ GNU Compiler Collection (GCC) เป็น font end และกลายเป็น llvm-gcc แต่ด้วยความซับซ้อนของ gcc รวมไปถึงการสนับสนุนภาษา Objectiv-C ใน gcc มีความสำคัญน้อยกว่าภาษาอื่น อีกทั้งปัญหาสัญญาอนุญาตสาธารณะทั่วไปของ gcc ที่กำหนดเป็น GPL version 3 ซึ่งมีข้อกำหนดที่สำคัญคือ ส่วนขยายของ gcc นั้นต้องเปิดเผยโค้ด เมื่อเป็นดังนี้ Apple จึงได้จ้าง Chris เข้ามาร่วมงาน และเริ่มโครงการพัฒนา font end ตัวใหม่ชื่อ Clang เพื่อใช้แทน gcc และในที่สุดจึงกลายมาเป็นคอมไพลเลอร์ Apple LLVM นั่นเอง ( จากจุดนี้เมื่อพูดถึง llvm จะหมายถึง Apple LLVM )

Autosynthesis of Properties

จากที่เคยได้อธิบายไปแล้วว่าในภาษา Objective-C นั้นพร็อพเพอร์ตี้เป็นเพียง accessor method รูปแบบหนึ่ง ซึ่งเป็นคนละอย่างกับ instance variable (ivars) หรือตัวแปรที่เป็นสมาชิกของคลาส เช่น

คลาส Student มีตัวแปรชื่อ name และมีพร็อพเพอร์ตี้ชื่อ name เช่นเดียวกัน ดังนั้นการกำหนดค่าให้กับตัวแปร name นั้นสามารถทำได้โดยเรียกผ่านตัวแปรโดยตรงและผ่านทางพร็อพเพอร์ตี้

จากโค้ดตัวอย่าง การกำหนดค่าให้กับตัวแปรโดยตรงและการกำหนดค่าผ่านพร็อพเพอร์ตี้ ไม่มีความแตกต่างกัน เพราะผลลัพธ์สุดท้ายเหมือนกัน ซึ่งอาจจะทำให้หลายคนเข้าใจผิดว่า self.name และ name ทั้งสองคือสิ่งเดียวกัน ย้ำอีกทีว่าทั้งสองอย่างนี้มีการทำงานนั้นต่างกัน เพราะพร็อพเพอร์ตี้จะเป็นการกำหนดค่าผ่านเมธอด setter ที่ถูกสร้างขึ้นมาจาก @synthesize ตามคุณสมบัติของพร็อพเพอร์ตี้ที่ได้กำหนดไว้ เพื่อให้เห็นภาพความแตกต่างระหว่างสองอย่างนี้อย่างชัดเจนมากขึ้น เราจะกำหนดข้อบังคับบางอย่างให้กับพร็อพเพอร์ตี้ โดยการเขียน setter ขึ้นมาเอง

และเมื่ออ็อบเจ็ก Student เรียกเมธอด printTotalCourse ก็จะได้ผลลัพธ์ดังนี้

Program 16.1 Output

Courses 0

Courses 3

Courses 3

ผลลัพธ์แสดงให้เห็นว่าการเรียกใช้ตัวแปรโดยตรงกับการใช้พร็อพเพอร์ตี้นั้นต่างกัน เมื่อ NSLog ทำงานครั้งแรก จะเรียกเมธอด count ผ่านอ๊อบเจ็ก courses โดยตรง แต่อ๊อบเจ็กยังไม่ได้ถูกสร้างขึ้นมา ค่าที่ได้จึงเป็น 0 จากนั้นเรียก count อีกครั้งแต่เป็นการใช้พร็อพเพอร์ตี้ และเนื่องจากเขียน setter ขึ้นมาเอง คอมไพลเลอร์จึงไม่สร้าง setter และใช้เมธอด courses ที่เขียนได้ขึ้น ดังนั้นอ็อบเจ็ก courses จึงถูกสร้างขึ้นมาด้วยเมธอดนี้นั่นเอง และเมื่อนับจำนวนสมาชิกก็จะได้ทั้งหมด 3 ตัว และส่วน count ในบรรทัดสุดท้ายของเมธอด printTotalCourse เป็นการใช้อ็อบเจ็ก course โดยตรงซึ่งก็ได้ผลลัพธ์ 3 เช่นเดียวกัน จะเห็นว่าการเรียกใช้อ๊อบเจ็กโดยตรงในครั้งแรกและครั้งที่สามนั้นให้ค่าไม่เท่ากัน ซึ่งอาจจะส่งผลทำให้โปรแกรมเกิดข้อผิดพลาดได้ ในการหลีกเลี่ยงปัญหาเช่นนี้อาจจะแก้ไขโดยการเรียกพร็อพเพอร์ตี้เสมอ แม้ว่าเราจะเลี่ยงปัญหาโดยเรียกผ่านพร็อพเพอร์ตี้แล้วก็ตาม แต่ถ้าโปรแกรมได้กำหนดให้ชื่อของพร็อพเพอร์ตี้กับชื่อของตัวแปรเหมือนกันดังตัวอย่าง ก็อาจจะทำให้เกิดข้อผิดพลาดที่พบได้บ่อยๆคือการพิมพ์ self ตกไป กลายเป็นเรียกตัวแปรโดยตรง เพื่อกันความผิดพลาดและป้องกันความสับสน โปรแกรมเมอร์ส่วนใหญ่จึงนิยมกำหนดชื่อตัวแปร โดยมีด้วยเครื่องหมาย _ (underscore) นำหน้า ดังเช่นตัวอย่าง

และในส่วนของ implementation ก็จะเขียน @synthesize ดังนี้

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

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

เมื่อเขียน implementation ไม่ต้องจำเป็นเขียน @synthesize และยังสามารถเข้าถึงตัวแปรได้โดยตรง ดังเช่นตัวอย่าง

จะเห็นว่าเราสามารถใช้ตัวแปร _program ที่คอมไพลเลอร์ได้สร้างขึ้นมาจากการประกาศพร็อพเพอร์ตี้ได้ และ autosynthesis of properties นี่ก็คือคุณสมบัติแรกจากการเปลี่ยนมาใช้ Clang สรุปง่ายๆก็คือ เมื่อประกาศพร็อพเพอร์ตี้ ไม่จำเป็นต้องเขียนโค้ด @synthesize เพราะคอมไพลเลอร์จะเพิ่มโค้ด @synthesize ให้กับคลาสโดยอัตโนมัตินั่นเอง

Literal Syntax

Objective-C literal ก็คือโค้ดที่ใช้ในการอ้างอิงถึง Cocoa Object แบบกำหนดเฉพาะเจาะจง และการอ้างอิงอ๊อบเจ็กนี้อาจจะมีการสร้างอ็อบเจ็กขึ้นมาใหม่ในกรณีที่จำเป็น เช่น

โค้ด @”Hello” คือการใช้ literal syntax ของคลาส NSString ซึ่งเหมือนการเรียกใช้ [NSString stringWithString:@”Hello”] นั่นเอง และในบทก่อนๆ เราก็เคยใช้ literal แบบอื่นไปบ้างแล้ว เช่นการใช้ block literal เป็นต้น สำหรับ literal syntax ใหม่ที่เพิ่มเข้ามานั้น เป็นของคลาส NSNumber , NSArray และ NSDictionary ซึ่งมีรูปแบบการใช้งานดังนี้

NSNumber
เนื่องจาก NSNumber เป็นอ็อบเจ็กไม่ใช่ตัวแปรแบบ primitive type แบบ int , float เมื่อใช้งานจึงต้องประกาศอ็อบเจ็กขึ้นมาก่อนดังเช่นตัวอย่าง

แต่ด้วย literal ใหม่สามารถเขียนโค้ดให้สั้นลงได้ เหมือนการประกาศใช้ตัวแปรแบบ primitive type ดังเช่นโค้ดตัวอย่างต่อไปนี้

NSArray
เมื่อต้องการจะประกาศอาร์เรย์พร้อมกับสมาชิกโดยทั่วไปจะเขียนโค้ดลักษณะดังนี้

แต่เมื่อเปลี่ยนมาใช้ literal syntax ใหม่ก็จะมีโค้ดที่สั้นลง

นอกโค้ดที่สั้นลงแล้ว สิ่งที่เปลี่ยนไปอีกอย่างก็คือ ไม่ต้องเขียน nil ต่อท้ายเหมือนการประกาศอาร์เรย์แบบเดิม

NSDictionary
คลาสสุดท้ายที่เพิ่ม literal ใหม่เข้ามาคือ NSDictionary การใช้งาน literal ใหม่ สามารถทำได้ดังเช่นตัวอย่าง

เริ่มต้นส่วนแรกด้วย key จากนั้นคั่นด้วยเครื่องหมาย : และตามด้วย value เป็นคู่ๆกันไป และไม่ต้องเขียน nil ต่อท้ายเช่นเดียวกันกับอาร์เรย์

Access Syntax

นอกจากการประกาศอาร์เรย์และดิกชันนารีด้วย literal ใหม่แล้ว การเข้าถึงสมาชิกใน  NSArray และ NSDictionary ก็มี syntax ใหม่เพิ่มขึ้นมาเช่นกัน อย่างอาร์เรย์จากเดิมที่ต้องใช้เมธอด objectAtIndex: เพื่อเข้าถึงสมาชิก ก็สามารถใช้เครื่องหมาย [ ] เหมือนกับการเข้าถึงสมาชิกของอาร์เรย์ในภาษา C แทนได้ ดังเช่นโค้ดต่อไปนี้

การใช้ syntax ใหม่ไม่ใช่เพียงแค่อ่านค่าจากอาร์เรย์เท่านั้น แต่ยังสามารถใช้เพื่อการเปลี่ยนแปลงค่าสมาชิกในตำแหน่งที่ต้องการของคลาส NSMutableArray ได้อีกด้วย ดังเช่นตัวอย่างโค้ดต่อไปนี้

เมื่อให้โปรแกรมทำงานก็จะได้ผลลัพธ์ดังนี้

Program Output

iPad

iPhone

Macbook

ส่วนการเข้าถึงสมาชิกของ NSDictionary ใช้เครื่องหมาย [ ] ได้เช่นเดียวกันกับอาร์เรย์ แต่ให้ระบุด้วย key แทนตำแหน่ง เช่น

ในกรณีที่ดิกชันนารีหรืออาร์เรย์เป็นลักษณะซ้อนกัน (nested) สามารถใช้ [ ] ซ้อนกันเพื่อเข้าถึงสมาชิกด้านในได้เช่นเดียวกัน

จากตัวอย่างได้ประกาศ product เป็นอาร์เรย์ที่มีสมาชิกเป็นอาร์เรย์เช่นกัน เมื่อเรียก product [0] [1] ก็จะหมายถึงการอ้างสมาชิกดังรูป

array_mem

และโค้ดการเข้าถึงสมาชิกของดิกชันนารี เช่น player[@”statistics”][@”score”] ก็มีลักษณะการทำงานเช่นเดียวกันกับ product ที่ได้อธิบายไป

ข้อควรจำและต้องระมัดระวังเมื่อใช้ literal และ access syntax ใหม่นี้คือ อ็อบเจ็กที่ถูกสร้างขึ้นมาจะเป็น immutable เสมอถึงแม้ว่าเราจะประกาศให้เป็น mutable ก็ตาม ดังเช่นตัวอย่างโค้ดต่อไปนี้

ถ้าหากให้โปรแกรมทำงานก็จะเกิด error เกิดขึ้น นั่นเป็นเพราะว่าอาร์เรย์ที่ประกาศแบบ literal นี้จะเป็น immutable หรือเป็น NSArray เสมอ ดังนั้นจึงไม่สามารถแก้ไขสมาชิกได้ ทำให้โปรแกรมจึงทำงานผิดพลาด

 

Class Extensions

class extension เป็นการขยายความสามารถให้กับคลาสเช่นเดียวกันกับแคทิกกอรี่ หรืออาจจะเรียกว่าเป็น anonymous category ก็ได้ เนื่องจากมีรูปแบบการประกาศเหมือนกับแคทิกกอรีที่ไม่ได้กำหนดชื่อ

class_exten

ถึงแม้ว่าจะมีรูปแบบที่คล้ายกัน แต่การเขียนเมธอดที่ได้เพิ่มเข้ามาใน class extensions นั้นต่างกับแคทิกกอรี่ เพราะการเขียน implement ของแคทิกกอรี่นั้นสามารถเขียนแยกจากไฟล์ implantation หลักได้ ยกตัวอย่างเข่น เมื่อสร้างคลาส Student จะได้ไฟล์ Student.m ซึ่งเป็นไฟล์หลักที่ไว้เขียนการทำงานของคลาส เมื่อประกาศแคทิกกอรี่เพิ่มเติมเช่น แคทิกกอรี่ Master ก็จะได้ไฟล์ Student+Master.m เป็นไฟล์ implement แยกออกมาไว้ให้เขียนการทำงานของแคทิกกอรี่นั้น
แต่ในกรณีของ class extension นั้น ต้องเขียนส่วนการทำงานต่างๆไว้ที่ implement หลักเท่านั้น ไม่สามารถแยกเขียนได้ ดังนั้นแล้ว การประกาศ class extension จึงมักจะประกาศที่ไฟล์ implement หลัก ดังเช่นตัวอย่างต่อไปนี้

Student.h

Student.m

สิ่งที่ต่างกันกับ category อีกประการคือสามารถเพิ่มตัวแปรโดยการประกาศพร็อพเพอร์ตี้ใน class extension ดังเช่น พร็อพเพอร์ตี้ school ดังที่แสดงในตัวอย่าง หรืออาจจะเพิ่มตัวแปรใหม่เข้าไปโดยตรงก็สามารถทำได้เช่นกัน

Private properties with Class Extensions

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

Program 16.3
Player.h

พร็อพเพอร์ตี้ของคลาส Player ที่ได้ประกาศไปมีทั้งหมด 3 ตัวด้วยกันคือ name , lastName และตัวสุดท้ายคือ age ซึ่งกำหนดให้เป็น readonly เพราะไม่ต้องการให้อ๊อบเจ็กอื่นเข้าแก้ไข และนั่นก็หมายถึงว่า Player เองก็ไม่สามารถแก้ไขได้เช่นกัน แต่ด้วยความสามารถของ class extension ทำให้ตัวเองสามารถแก้ไขได้พร๊อพเพอร์ตี้ ด้วยการกำหนด attribute ใหม่ ดังนี้

Player.m

ในส่วนของ implementation นั้นได้ประกาศ  class extension ขึ้นมาพร้อมกับตัวแปรใหม่ชื่อ fullName และกำหนด attribute ของพร็อพเพอร์ตี้ age ใหม่ให้เป็น readwrite แทนของเดิมคือ readonly การทำเช่นนี้จะทำให้ตัวเองสามารถแก้ไขพร็อพเพอร์ตี้ age ได้เพียงคนเดียวดังที่แสดงในบรรทัดที่ 15 ส่วนอ็อบเจ็กอื่นก็จะไม่สามารถแก้ไขค่าได้เช่นเดิม เพราะใช้ attribute จาก interface ที่ได้กำหนดเป็น readonly

main.m

Program 16.3 Output

Name: Hakaru Miyashita
age: 19

Hakaru Miyashita 19

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

arc_error

ข้อสำคัญที่ควรจำของ class extension คือ เมื่อประกาศพร็อพเพอร์ตี้ใหม่ โดยที่ไม่ได้ประกาศไว้ใน interface เช่นพร็อพเพอร์ตี้ school ของคลาส Student ที่ผ่านมา จะเป็นการประกาศแบบ private ทำให้อ็อบเจ็กอื่นมองไม่เห็นและไม่สามารถเข้าใช้พร็อพเพอร์ตี้นั้นได้

 

4 thoughts on “Objective-C Programming Chapter 16 (Part1)”

Leave a Reply