@escaping ตัวร้ายกับนาย closures ใน Swift Language

Pattaravadee Luamsomboon
2 min readApr 17, 2020

สวัสดีชาวโลก วันนี้มาด้วย blog ภาษาไทย เพราะเป็นเรื่องที่ค่อนข้างเข้าใจได้ยาก ถ้าเขียนเป็นภาษาอังกฤษอาจจะงงทั้งคนอ่านและคนเขียน แต่ถ้าเขียนเป็นไทยก็จะยังงงทั้งคู่อยู่ดี 🤣🤣🤣 หยอกๆ เอาล่ะมาเข้าเรื่องกันเลยดีกว่า วันนี้จะมาเขียนเรื่องของ @escaping คนเขียน swift lang น่าจะเคยเห็นกันมาบ้างตอนที่ต้องใช้ closure แต่ถ้าไม่เคยเขียน swift คงงงว่านี่มันคืออะไร closures คืออะไร @escapingคืออะไร ตอนไหนล่ะเราถึงจะต้องใส่ @escapingเข้าไปใน closures แต่ถ้าอ่าน blog นี้จบ ไม่ว่าจะเคยเขียนหรือไม่เคยเขียน swift ก็จะพอนึกออกเลยแหละว่าสิ่งนี้คืออะไร แต่ถ้านึกไม่ออกมาทำงานที่ FINNOMENA ดิ เดี๋ยวเราสอนให้ 😁😁😁

เอาล่ะมาดูสิ่งที่จะพูดถึงใน blog นี้กัน

  • Closures คือสิ่งใด
  • @nonescaping ทำงานยังไง ใช้ตอนไหน
  • @escaping ทำงานยังไง ใช้ตอนไหน

Closures คืออะไร

ขอมามุกเดิมก่อนเลย แปะ doc ที่ได้กล่าวไว้

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

จาก doc เราสามารถจินตนาการตามได้ว่า closures มันก็คล้ายกับ block ใน c และ obj-c หรือ lambdas ใน programming language อื่นๆ นั่นแหละ แต่ขออธิบายแบบภาษาบ้านๆ เพิ่มนิดนึงตรง self-conself-contained blocks of functionality เนี่ย มันก็คือการที่เรา ส่ง function เข้าไปเป็น 1 ใน parameter ของอีก function ได้เลยยังไงล่ะ แล้วทีนี้ @nonescaping กับ @escaping เนี่ยถ้าเราจะส่ง parameter เข้ามาเป็น function ก็อาจจะต้องเขียน @escaping หรืออาจไม่ต้องเขียนก็ได้ แล้วแต่การทำงานซึ่งจะพูดถึงในหัวข้อถัดไป

@nonescaping ใช้ตอนไหน ทำงานยังไง

จากด้านบนที่เราได้รู้ไปแล้วเนอะว่า closures คืออะไร ทีนี้เรามาดูกันบ้างว่าตัว @nonescaping จะเอาไปใช้ตอนไหน และทำงานยังไง สำหรับตัว @nonscaping เราไม่จำเป็นต้องเขียนลงใน function เพราะจะถูก default ไว้อยู่แล้ว เดี๋ยวลองดูตามตัวอย่างโค้ดด้านล่างแล้วคิดตามนะ

  • step 1 เราเรียก calculateBMI โดยจะส่งความสูง, น้ำหนัก และ result เข้าไปซึ่งถูกรับเป็น function ที่จะ return ค่า bmi มาให้
  • step 2 ใน calculateBMI function จะคำนวณ bmi
  • step 3 result function จะถูกเรียกเป็น callback และแนบค่า bmi กลับมาด้วย
  • step 4 เอาค่า bmi มาใช้

จะเห็นว่าตัว result จะถูกเรียก callback กลับมาตั้งแต่ใน body ของ calculateBMI function แล้ว การทำงานทุกอย่างก็คือจบแล้ว

ตัวอย่างที่ 1

override func viewDidLoad() {   super.viewDidLoad()
// Step 1
calculateBMI(height: 1.58, weight: 44.5, result: { bmi in // Step 4
print(bmi)
})
}func calculateBMI(height: Float,
weight: Float,
result: (_ value: Float) -> Void) {
// Step 2
let bmi = weight/pow(height, 2)
// Step 3
result(bmi)
}

@escaping ใช้ตอนไหน ทำงานยังไง

สำหรับตัว @escaping มองแบบบ้านๆ คือ function ที่เราส่งเป็น parameter สามารถแบ่งได้เป็น 2 อย่างที่จะทำให้เราจำเป็นต้องใส่ @escaping เข้าไป

  1. function ที่ส่งไปเป็น parameter ถูกเก็บใส่เป็น variable ไว้ (ดูตัวอย่างที่ 2) จะเห็นว่าเมื่อเราส่ง result parameter เข้าไปใน calculateBMI function ตัว result จะถูกเก็บไว้ใน resultHandler ที่เป็น variable ก่อน แล้วมาเรียกใช้ตอนไหนก็ได้
  2. ถูกเรียกใน Asynchronous excution ซึ่งอาจจะเป็นใน dispatch queue ก็ได้ (ดู ตัวอย่างที่ 3) จะเห็นว่าตัว resut parameter ถึงแม้จะถูกเรียกใน calculateBMI แต่มันถูกเรียกในอีก 2 วิ ทำให้ตัว result ต้องถูกถือไว้ใน memory ก่อนจนกว่าจะถูกเรียก

ตัวอย่างที่ 2

var resultHandler: (Float) -> Void = { _ in }override func viewDidLoad() {    super.viewDidLoad()    calculateBMI(height: 1.58, weight: 44.5, result: { bmi in        print(bmi)    })}func calculateBMI(height: Float,                  weight: Float,                  result: @escaping (_ value: Float) -> Void) {     resultHandler = result     let bmi = weight/pow(height, 2)     anotherFunction(bmi: bmi)}func anotherFunction(bmi: Float) {    resultHandler(bmi)}

ตัวอย่างที่ 3

override func viewDidLoad() {    super.viewDidLoad()    calculateBMI(height: 1.58, weight: 44.5, result: { bmi in        print(bmi)
})
}func calculateBMI(height: Float, weight: Float, result: @escaping (_ value: Float) -> Void) { let bmi = weight/pow(height, 2) DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { result(bmi)
}
}

เอาล่ะจาก 3 ตัวอย่างด้านบนขอสรุปอีกครั้งให้เข้าใจมากขึ้นละกันนะ หรืออาจจะงงกว่าเดิม 55555 เอาง่ายๆ ถ้า function ที่ถูกส่งเข้ามาเป็น parameter มีการ keep ค่าไว้ ไม่ว่าจะเป็นด้วย variable หรือการรอ queue เพื่อให้ callback ถูกเรียก เราจำเป็นต้องใส่ @escaping ไว้ที่ parameter ด้วย

หวังว่าบทความนี้จะมีประโยชน์กับทุกคนที่เข้ามาอ่านจนจบนะ 🙇🏻‍♀️🙇🏻‍♀️🙇🏻‍♀️

--

--