@escaping ตัวร้ายกับนาย closures ใน Swift Language
สวัสดีชาวโลก วันนี้มาด้วย 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
เข้าไป
- function ที่ส่งไปเป็น parameter ถูกเก็บใส่เป็น variable ไว้ (ดูตัวอย่างที่ 2) จะเห็นว่าเมื่อเราส่ง result parameter เข้าไปใน calculateBMI function ตัว result จะถูกเก็บไว้ใน resultHandler ที่เป็น variable ก่อน แล้วมาเรียกใช้ตอนไหนก็ได้
- ถูกเรียกใน 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 ด้วย
หวังว่าบทความนี้จะมีประโยชน์กับทุกคนที่เข้ามาอ่านจนจบนะ 🙇🏻♀️🙇🏻♀️🙇🏻♀️