Objective-C’s category

มกราคม 4, 2011 at 3:57 pm 5 ของความคิดเห็น

ผมค่อนข้างแปลกใจ ที่ทำไมผมไม่เคยเขียนเรื่องของ Category ใน Objective-C เลย ทั้งๆที่เป็นสิ่งที่ผมชอบมากที่สุดสิ่งนึงใน ObjC เลยนะ เอาเถอะ งั้นวันนี้ก็เขียนเลยแล้วกัน เป็น Entry iPhone Apps Dev รับปีใหม่ที่ห่างหายไปนานซะเหลือเกิน

บางครั้ง เวลาที่เรา Initial object จาก Class ขึ้นมา 1 ตัว เช่น NSArray คงมีบางทีที่เราอยากให้มันมี Method อื่นๆที่ apple ไม่ได้ provide มาให้เรา อาจจะเช่น การ sum value ของ NSNumber ทั้งหมดที่อยู่ใน array, การเรียงข้อมูลด้วยวิธีที่นอกเหนือจากที่มีมาให้ ฯลฯ แล้วแต่ความต้องการของ App ใช่มั้ย

ซึ่งพอเป็นอย่างนี้ ต่างคนต่างก็มีวิธีต่างกันไป บางคนก็วนลูปจัดการใน Class ของตัวเอง บางคนก็ Subclass สร้าง Array class ที่มี method เหล่านั้นอยู่ แต่ที่ผมจะเสนอใน entry นี้คือ “การใช้ Category”

Category เป็น Design Pattern รูปแบบนึง ซึ่งในภาษาอื่นผมก็ไม่ทราบเหมือนกันครับ ว่ามันคือ Pattern ชื่อว่าอะไร แต่อธิบายสั้นๆง่ายๆก็คือ เป็น Pattern ที่เอาไว้เพิ่ม method ให้กับ Class นั้นๆนั่นเอง ทำไมต้องเพิ่ม? เพราะในการ develop app นั้น มีความเป็นไปได้ว่า Class ที่มีมาให้นั้นไม่มีการทำงานแบบที่เราต้องการ อาจเป็นโค้ดจาก apple หรือเป็นโค้ดจากทีมอื่นๆส่งมาให้เรา ซึ่งเราไม่ทราบว่า เขา Implement เอาไว้อย่างไร เป็นต้น ซึ่งในกรณีนี้ Category ช่วยได้มากครับ

มาดูหน้าตาของ Category กันเลยดีกว่า

ส่วน header (.h)

@interface NSArray (MyCategory)

– (void)additionMethod1

@end

ส่วนของ implement (.m)

@implementation NSArray (MyCategory)

– (void)additionMethod1

{

// Do something …

}

@end

เป็นไงครับ ไม่ยากและแทบไม่มีอะไรแตกต่างกับการ subclass เลยใช่มั้ย ต่างกันนิดเดียว ตรงหัว @implement กับ @interface เท่านั้น จากปกติที่เคยมีลักษณะว่า

@interface MyClass : NSArray แปลว่า Array ตัวเนี่ย มันเป็น NSArray ที่กลายพันธุ์มาแล้ว มันมี a, b แล้วก็ c ด้วย แถมยังทำ y กับ Z ได้อีก จนมันเป็นสิ่งใหม่ที่ชื่อ MyClass ไปแล้ว

@implementation MyClass แปลว่า MyClass class ของฉันเนี่ย มันทำงานแบบนี้ๆๆๆ…

กลายเป็นความหมายประมาณว่า

@interface NSArray (myCategory) แปลว่า NSArray สายพันธุ์ myCategory เนี่ย มันก็ยังหน้าตาเหมือนเดิมนี่แหละ แต่ทำ y กับ z เพิ่มมาได้ด้วย แต่มันก็ยังเป็น NSArray อยู่นะ

@implementation NSArray (myCategory) แปลว่า NSArray สายพันธุ์ myCategory เนี่ย ที่มันต่างจากเดิมเนี่ย ที่ต่างคือมันทำงานงี้ๆๆนะ

…การแปลของผม มันทำให้เข้าใจง่ายขึ้นหรือยากลงก็ไม่รู้สินะ แต่สิ่งที่ผมต้องการโฟกัสก็คือ ความที่มันเป็นสิ่งใหม่กับเป็นสิ่งเดิมมากกว่า สิ่งที่ Category แตกต่างกับ Subclass ได้แก่

1. subclass -> child class, category -> upgraded parent class – บางคนอาจไม่ได้รู้สึกว่ามันสำคัญอะไร แต่สำหรับการพัฒนา app ขนาดใหญ่ๆ หรือมีความซับซ้อน Category จะช่วยให้เราไม่ต้องไปไล่แก้ Class ทั้งโปรเจคให้เป็น Class ใหม่ที่เราเขียนเพิ่ม method เข้าไป (อย่างแย่ก็แค่ไล่ import category เท่านั้นแหละ)

2. subclass เพิ่ม ivar ได้, category เพิ่ม ivar ไม่ได้ – ตามนั้นครับ นี่เป็นข้อจำกัดของ category แต่ trick ที่ทำให้ใช้ สร้าง ivar ได้ มันก็ไม่ใช่ไม่มี …แต่ก็ไม่ใช้ ivar แท้หรอก

จากที่อธิบายข้างบน ดูแล้วอาจมองแค่ว่า เพิ่ม method ที่ใช้บ่อยๆกับ Standard class ก็โอเคแหละ …แต่จริงๆแล้ว การประยุกต์ใช้ Category สามารถทำได้หลายแบบมากๆ ดังเช่น

1. ใช้ category ในไฟล์ .m เพื่ออธิบายกับ compiler ว่ามี method ใดบ้างใน class นั้น ซึ่ง method เหล่านั้นอาจไม่ได้ใส่ไว้ใน header ไฟล์ เพราะต้องการใช้แค่ภายในเท่านั้น private method นั่นแหละ การทำแบบนี้ จะทำให้ compiler ไม่ขึ้น warning หรือแถบเหลืองๆ ตอน compile เสร็จกับ method ที่เราเรียกใช้ก่อน implement method นั้น การใช้งานก็จะไม่ระบุ categoryName ซึ่งภาษา ObjC อนุญาตให้มีแบบนี้ได้ 1 อันเท่านั้น ลักษณะเป็นแบบนี้

.m file

@interface MyClass ()

– (void)privateMethod1;

@end

@implementation MyClass

– (void)mainMethod1 {

[self privateMethod1];

}

– (void)privateMethod1 {

// Do something Private

}

@end

2. ใช้ในการปรับ Standard class จาก Apple ให้ตรงความต้องการ และเพื่อการ reuse ไว้ใช้ซ้ำ นี่เป็นข้อดีมากๆข้อนึงของ category ครับ คือ reuse ง่าย อยากให้ NSArray สุ่มตัวเลขได้ หรือ NSString เป็น JSON parser ได้ ก็ทำได้ง่ายๆแถม reuse ใช้ง่ายด้วย

3. Fix bug ใน framework Category ไม่ได้แค่สามารถเพิ่ม method ให้กับ Class ได้ แต่สามารถ override method ของคลาสได้ด้วยเช่นกัย ประโยชน์ข้อนี้เราสามารถเอามา fix bug ใน framework ได้ครับ หรือโดยส่วนตัว ผมก็ใช้ category override method ของคลาสที่เขียนขึ้น เวลาเพิ่ม feature หรือ fix bug (แม้ว่าจะเป็น code ตัวเองทั้งหมดก็ตาม) เพราะมันช่วย safe ให้ผมไม่ทำอะไรกระทบกระเทือนกับตัวเก่าที่มันดีอยู่แล้ว ถ้าทำไปทำมาเจ๊ง ก็แค่ลบ category นั้นทิ้งไป คล้ายๆกับ version control ยังไงยังงั้น

ข้อควรระวังของ category ก็มีอยู่เหมือนกันครับ อย่างแรกคือ “ลำดับ” ถ้า class หนึ่งมี category หลายๆตัว และ category หลายๆตัวก็ implement method ซ้ำกันอยู่ compiler จะถือเอาตัวที่ import ตัวสุดท้ายเป็นหลัก และอีกข้อ ชื่อ category ถ้าไปเหมือนกับของ apple ถ้าเป็น iOS ก็โดน reject app ครับ ถึงโอกาสจะน้อยแต่ก็ไม่ใช่ไม่มี วิธีหลีกเลี่ยงก็คือการใส่ prefix เอาไว้ใน category name ด้วย แค่นี้ก็พ้นแล้ว

…ฮ้าาาา ยาวมากกก เรื่องเล็กๆที่เขียนได้ไม่เคยสั้นเลย Category เป็นสิ่งที่มีประโยชน์มากจริงๆและเรียกได้ว่าเป็น “พลัง” ของ objective-c เลยก็ว่าได้ ผมชอบมันมาก ด้วยเหตุผลข้อ 3 มากที่สุดเลยล่ะ เอาล่ะ ยาวมากๆแล้วสำหรับ entry นี้ ขอให้สนุกกับการใช้ category แล้วกันนะครับ Happy coding ครับ

 

 

Entry filed under: Cocoa Programming, Computer, iPhone Programming, Tips & Techniques.

Intention 2011 แพ้คำว่ารัก : Calories Blah Blah – Love Delivery

5 ความเห็น Add your own

  • 1. memogames  |  มกราคม 4, 2011 ที่ 4:41 pm

    สุดยวด

    ตอบกลับ
  • 2. mai  |  กุมภาพันธ์ 8, 2011 ที่ 12:47 pm

    พี่ค่ะโปรเจคมันสามารถรันกับเวอร์ชั่นที่ต่างกันได้ไหมค่ะ พอดีทำอีกเวอชั่นหนึ่งแล้วมารันอีกเวอชั่นหนึ่งแล้วมันรันไม่ได้ค่ะ T___T รบกวนด้วยค้าบ

    ตอบกลับ
    • 3. Jerapong Nampetch  |  กุมภาพันธ์ 8, 2011 ที่ 3:29 pm

      เวอร์ชั่นต่างกันแบบไหน แล้วต่างกันแค่ไหนหล่ะครับ?

      ถ้าต่างกันแบบ iPhone/iPad ไม่น่ามีปัญหานะครับ เว้นแต่มีการใช้คำสั่งของ iPad เช่น UISplitView อะไรพวกนี้

      แต่ถ้าต่างกันแบบว่า iOS3 กับ iOS4 ก็ต้องลองเช็คว่ามีการใช้ method อะไรที่มัน deprecate ไปแล้วรึเปล่า ดูจาก warning

      ส่วนถ้า run ไม่ได้เพราะลง SDK ใหม่ ไปเซต deployment target ให้ตรงกับ device เราก็ build ได้แล้วครับ ไปที่ project setting แล้วก็ search setting ตัวนี้เอา

      คงตอบระบุละเอียดไม่ได้นะครับ เพราะไม่ได้บอกมาว่ามัน Error ยังไง

      ตอบกลับ
  • 4. ae  |  เมษายน 20, 2011 ที่ 9:21 am

    ขอบคุณมากครับ บทความสุดยอด ลึกถึงใจจริงๆ
    ผมไม่เข้าใจ “ใช้ category ในไฟล์ .m เพื่ออธิบายกับ compiler ว่ามี method ใดบ้างใน class” อ่ะครับ หมายความว่ายังไง

    ตอบกลับ
    • 5. Jerapong Nampetch  |  เมษายน 20, 2011 ที่ 5:36 pm

      ถ้าอย่างในภาษา C ก็คือ Preprocessor Directive อ่ะครับ …งงมั้ยครับ ถ้างงก็อธิบายต่อให้ครับ

      ลักษณะภาษาที่เป็น Sequencial Programming อย่างภาษา C หรือ Obj-C ก็ตาม compiler จะทำความเข้าใจกับโค้ด “จากบนลงล่าง(Top down)” ครับ

      ผมกำหนดให้โค้ดของเรามีฟังก์ชั่น 2 ฟังก์ชั่น คือ

      – (void)funcA
      { // Do something A }

      – (void)funcB
      { // Do something B }

      – ถ้าเกิดว่า funcB มีการเรียกใช้ funcA compiler จะทำงานได้ตามปกติ เพราะรู้จัก funcA แล้ว จากบรรทัดด้านบน
      – แต่ถ้า funcA เรียก funcB compiler จะขึ้น Warning กับเราว่ายังไม่รู้จักกับ funcB (ซึ่งอยู่ด้านล่าง) และถ้ายังจะ run ก็อาจจะมีปัญหาเพราะไม่รู้จัก func นั้น

      วิธีการการันตีกับ compiler ว่า funcB มันมีอยู่จริงตั้งแต่แรกแล้ว จึงทำได้โดยการนิยาม funcB ไว้ในไฟล์ .h เท่านี้ compiler ก็รู้จักแล้วมี funcB (compiler อ่าน .h ก่อน .m)

      แต่มันก็เป็นการเปลี่ยนแปลง Interface ของ class เราด้วยเช่นกัน การเปิดทุกฟังก์ชั่นให้เรียกใช้ได้ ไม่ใช่เรื่องดีและจำเป็นครับ ยิ่งถ้าเป็นโปรแกรมขนาดใหญ่ๆ มีคนเขียนมากกว่า 1 คน ยิ่งไม่ควร เปิดเผยเท่าที่จำเป็นก็พอครับ ดังนั้น category จึงถูกประยุกต์ใช้ในการแก้ปัญหานี้ครับ :)

      ตอบกลับ

ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out /  เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out /  เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out /  เปลี่ยนแปลง )

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed


del.icio.us For iPhone dev

Post Calendar

มกราคม 2011
พฤ อา
« ธ.ค.   ก.พ. »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

%d bloggers like this: