Nullability and Objective-C

เรื่องดีอย่างหนึ่งบน Swift ที่ถูกพูดถึงคือการทำงานกับโค้ด Objective-C ได้อย่าง(ค่อนข้างจะ)ไร้ปัญหา ทั้ง Library ที่ถูกเขียนด้วย Objective-C มาก่อน หรือโค้ดใน project เราเอง แต่อย่างไรก็ตาม ใน Swift มีคุณสมบัติหนึ่งที่เรียกว่า Optional ซึ่งจะแยกชัดเจนระหว่าง object ที่เป็น nil ได้ หรือไม่ได้ อย่างเช่น UIView และ UIView? ขณะที่ Objective-C เมื่อก่อนจะไม่แยกเลยใช้ UIView * ในทั้งสองรูปแบบ ทำให้คอมไฟเลอร์ของ Swift ไม่สามารถมั่นใจได้ว่า UIView ดังกล่าวเป็น nil หรือไม่ ดังนั้นเมื่อ Objective-C object ถูกส่งผ่านมาใช้ในโค้ดภาษา Swift จะมาในรูปแบบ Implicitly unwrapped optional หรือ UIView! 

แต่ใน Xcode 6.3 ที่พึ่งออก Beta มาพร้อม Swift 1.2 นั้น ในฝั่งของ Objective-C ได้เพิ่มคุณสมบัติที่เรียกว่า nullability annotations เพื่อให้โค้ด Objective-C ที่ถูกนำมาใช้บน Swift ทำงานด้วยกันได้อย่างราบรื่นมากขึ้น

The Core: __nullable and __nonnull

ในฟีเจอร์นี้ เรามี annotation ใหม่เพิ่มขึ้นมา 2 ตัวนั่นคือ __nullable และ __nonnull ซึ่งเราน่าจะเดาได้ว่า __nullable คือ pointer ที่จะชี้หาสิ่งที่อาจจะเป็น NULL หรือ nil ได้ และ __nonnull นั้นก็ใช้ในทางกลับกัน โดยคอมไพเลอร์จะเตือนเรา ถ้าเราไม่ทำตามกฎที่ได้ตั้งไว้ เช่น


เราสามารถใช้ __nullable และ __nonnull แทบจะทุกที่ และสามารถใช้ร่วมกับ const (C constant) ได้ด้วย อย่างไรก็ตามการใช้งานตามปกติที่ควรจะใช้ annotation พวกนี้คือตอนประกาศ method ซึ่งจะใช้ในรูปแบบที่ไม่มี __ นั่นคือ nullable และ nonnull หลังวงเล็บเปิด โดยจะต้องใช้กับ object หรือ block pointer เท่านั้น

สำหรับการประกาศ property ก็ใช้ในรูปแบบเดียวกัน


ดูเหมือนว่าการไม่มี __ จะเป็นรูปแบบที่ดีกว่า แต่คุณจะต้องใส่ annotation เหล่านี้ในทุกๆ ไฟล์ header ซึ่ง Apple ก็ได้มีวิธีการที่จะช่วยให้เคลียร์และง่ายขึ้น โดยการใช้ฟีเจอร์ที่ชื่อว่า Audited Regions

Audited Regions

เพื่อความง่ายในการใช้ annotation ใหม่นี้ เราสามารถที่จะใช้ฟีเจอร์ที่ชื่อว่า Audited Regions ในการบอกว่าในไฟล์ header นี้ property ทุกตัวจะเป็น __nonnull ทั้งหมด ส่วนตัวไหนที่มันจะต้องเป็น __nullable ก็ประกาศแยกไปอีกที แบบนี้


เพื่อความปลอดภัยในการใช้งาน Audited Regions ใหม่นี้ ก็มีกฎอยู่ 3 ข้อในการใช้งาน

  • ไม่สามารถใช้กับ typedef ได้ เนื่องจากมันมีโอกาสที่จะเป็นได้ทั้งคู่ ขึ้นอยู่กับสภาพแวดล้อมในตอนนั้น ดังนั้น typedef จะไม่ถูกถือว่าเป็น nonnull ในพื้นที่ Audited Regions
  • pointer ที่ซับซ้อนอย่าง id * จะต้องใส่ annotation เอง ตัวอย่างเช่น การประกาศ pointer ที่ไม่สามารถเป็น nil ได้ เพื่อชี้ไปหา object ที่เป็น nullable อย่าง id * จะประกาศแบบนี้ __nullable id * __nonnull
  • NSError ** มักจะถูกใช้เป็นค่าที่ถูกส่งกลับผ่าน method parameters นั่นคือ NSError จะถูกให้ค่าเป็น nullable เสมอ โดยสามารถอ่านเพิ่มเติมได้ที่ Error Handling Programming Guide
Compatibility
แล้วถ้าหากมี framework ที่เราใช้งานอยู่ ไม่เขียนตามกฎด้านบนนี้ล่ะ มันจะปลอดภัยไหม เมื่อถูกนำไปใช้งาน
  • โค้ดที่ใช้ framework ดังกล่าวสามารถที่จะทำงานต่อได้ เช่น framework ที่ชื่อว่า ABI ไม่ได้เปลี่ยนแปลง นั่นหมายความว่าโค้ดจะไม่ตรวจจับการส่งค่า nil ในระหว่าง runtime
  • โค้ดที่ใช้ framework ดังกล่าวอาจจะเกิด warning เมื่อมีการใช้งานในทางที่อาจจะก่อให้เกิดความไม่ปลอดภัย ผ่านคอมไพเลอร์ตัวใหม่ของ Swift
  • __nonnull ไม่มีผลต่อการทำ optimization คุณสามารถที่จะตรวจสอบ parameter ว่ามันเป็น nil ในขณะ runtime ได้ นี่อาจจะเป็นสิ่งจำเป็นในการทำ backward compatibility
คุณอาจจะรู้สึกแปลกๆ กับ __nullable และ __nonnull annotations ถ้าในตอนนี้คุณใช้คำสั่งพวก assertion และ exception ส่วนใหญ่ความผิดพลาดจะเกิดจาก programmer โดยเฉพาะค่าที่ถูกส่งกลับมาฟังก์ชั่นเป็นอะไรที่ควบคุมได้

Back to Swift 
ตอนนี้เราสามารถใส่ nullability annotations ให้โค้ด Objective-C แล้ว มาดูโค้ดหลังจากที่นำใช้งานใน Swift กัน ก่อนอื่นมาดูเวอร์ชั่นที่ไม่ใส่กันก่อน


และนี่คือเวอร์ชั่นที่ใส่เรียบร้อย

Popular posts from this blog

12 วิธี การบริการและดูแลลูกค้าในร้าน Starbucks

[Android Dev] การติดตั้ง Eclipse+AndroidSDK เพื่อพัฒนาโปรแกรมบน Android

"อีสุกอีใส" ประสบการณ์เมื่อต้องมาเป็นตอนอายุ 22