iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
生成式 AI

iOS? AI-Yes!:用 Vide Coding 加速我的 Swift 學習曲線系列 第 3

Day 3 - 語法核心 (二):深入解析 Optional (?) 、函式 (Function) 與閉包 (Closure)

  • 分享至 

  • xImage
  •  

昨天我們輕鬆地掌握了 Swift 的基礎。但今天的目標不是「精通」,而是勇敢地面對它們,並借助我們的 AI 家教,嘗試理解它們是什麼、為什麼存在,以及最基本的用法

挑戰難關前,先備好官方地圖

面對困難的主題,我們更需要官方文件來作為最終的參照。這能確保我們不會被 AI 或網路上的各種說法帶偏。


Optional (?)

  • 困惑點:在 Day 2,我們知道變數是用來存值的。但為什麼要設計一個『可能沒有值』的變數?這不是自找麻煩嗎?一個問號 ? 到底代表什麼?

  • AI 協作

    • 我的 Prompt:請用一個簡單的生活比喻,簡單解釋 Swift 的 Optional 是什麼,以及為什麼它很重要?

    AI的回答:

    生活比喻:查詢「附近有沒有咖啡廳?」

    想像你用地圖 App 查詢「我附近最近的咖啡廳」。這時有兩種可能的結果:

    1. 找到了:App 回傳給你一家咖啡廳的資訊(店名、地址、距離)。
    2. 找不到:你可能在深山或沙漠,附近真的沒有任何咖啡廳。App 回傳「無結果」。

    在 Swift 的世界裡:

    • Optional 就是那個「查詢結果」。它是一個容器,代表著「一個可能存在、也可能不存在的答案」。
    • 有值 (Value) 就是 App 找到了「星巴克,在 100 公尺外」。
    • nil 就是 App 回傳的「無結果」。
    // 註解:一個用來存放查詢結果的容器,結果可能是咖啡廳的名字(String),也可能沒有(nil)。
    var nearestCafe: String?
    
    // 情況一:找到了
    nearestCafe = "星巴克"
    
    // 情況二:沒找到
    nearestCafe = nil
    

    你不能直接假設一定有咖啡廳然後就直接動身前往,因為你可能會撲空。你必須先確認查詢結果


    為什麼這個「查詢結果」很重要?

    核心原因:為了避免「白跑一趟」的窘境,也就是程式的「閃退 (Crash)」。

    1. 現實中的問題:在沒有 Optional 概念的舊時代,程式碼會很天真地「假設」一定能查到咖啡廳。如果結果是「無結果」(nil),但程式下一步卻要「取得咖啡廳的地址並導航」,因為根本沒有地址,程式就會因為指令衝突而崩潰閃退。這是軟體中最常見的錯誤之一。

    2. Swift 的解決方案:Swift 語言強制規定,任何一個 Optional 的「查詢結果」,你都必須先檢查它是不是 nil,然後才能使用裡面的值。

      它強迫你思考:「如果找不到咖啡廳,我該怎麼辦?」

      • 是顯示「附近沒有咖啡廳」的提示?
      • 還是擴大搜尋範圍再找一次?

      這種「被強迫的深思熟慮」發生在程式編寫階段,而不是等到使用者用到一半,App 突然閃退造成糟糕的體驗。

    總結來說,Optional 的重要性在於:

    它是一種編譯時期的安全契約。透過在變數型別加上一個 ?,它明確地告訴開發者:「這個值不保證存在,請務必做好它不存在時的應對方案!」

    這個設計從根本上提升了程式碼的健壯性 (Robustness)安全性 (Safety),將最常見的執行期錯誤(因空值引發的閃退)轉化為編譯時期的語法檢查,讓開發者寫出更可靠的程式。

    • 我的理解是Swift 的 Optional 透過強制開發者在編譯時期就必須處理「值可能不存在」的情況,從根本上避免了因空值 (nil) 而導致的程式閃退,這真的很重要,以後可能會很常用到。

函式 (Function)

  • 困惑點:在什麼時候需要把程式碼包成一個『函式』?

  • AI協作:

    • 我的Prompt:簡單說明在Swift中函式扮演得角色

    AI的回答

    好的,簡單來說,在 Swift 中,函式扮演著程式的「基本建構單位 (Building Block)」與「可重複使用的工具」這兩個核心角色。

    您可以把每一個函式想像成一個功能單一的迷你工廠

    • 您給它一些原料(輸入的參數)。
    • 它依照設定好的生產線(函式內的程式碼)進行加工。
    • 最後產出一個成品(輸出的回傳值)。

    它的主要目的,就是將一個龐大、複雜的程式,拆解成許多個目標明確、可管理、可重複呼叫的小任務,藉此大幅提升程式碼的組織性、可讀性與可維護性

    • 我的理解是函式就是把常用的功能打包起來,取個名字。以後需要時,直接呼叫它的名字就好。這讓程式碼可重複使用更易於管理

閉包 (Closure)

  • 困惑點:我承認,這個部分我真的卡了最久。我看到很多程式碼裡有一對 {},裡面直接就是程式,它沒有名字,還能被當成參數傳來傳去。這完全超出了我的理解範圍。

  • AI 協作

    • 我的 Prompt:簡單解釋 Swift 的閉包是什麼?它和函式到底是什麼關係?

    AI的回答

    好的,這是一個 Swift 中非常核心且強大的概念。我們用一個簡單的比喻來拆解它。

    什麼是閉包 (Closure)?

    簡單來說,閉包就像一張「可以隨身攜帶的指令便條紙」。

    這張便條紙有兩個特點:

    1. 寫著要做的事:上面寫著一組指令(也就是一段程式碼)。
    2. 記得寫下時的周遭事物:如果寫便條紙時,旁邊有一杯「紅茶」,便條紙上寫著「喝掉桌上的飲料」,那無論你把這張紙帶到哪裡,它都記得當初要喝掉的是那杯「紅茶」。

    這個「記得周遭事物」的特性,在程式中稱為「捕獲 (Capture)」其上下文 (Context) 中的常數或變數。即使定義閉包的原始作用域已經不存在了,它依然能存取和操作當初被它「捕獲」的變數。

    
    func createAGreeter() -> () -> Void {
        // 註解:定義一個在函式內的變數
        let name = "路人甲"
    
        // 註解:這就是閉包,像一張「待辦事項」的便條紙
        // 它被回傳出去後,依然「記得」外面的 name 是 "路人甲"
        // 這就是「捕獲」
        let greeter = {
            print("你好, \(name)!")
        }
    
        return greeter
    }
    
    // 註解:取得這張便條紙 (閉包)
    let savedGreeter = createAGreeter()
    
    // 註解:createAGreeter 函式已經執行完畢,照理說 name 變數應該消失了
    // 但因為被閉包捕獲了,所以我們執行便條紙上的指令時,它依然有效
    savedGreeter() // 會印出:你好, 路人甲!
    

    它和函式 (Function) 到底是什麼關係?

    這是一個非常好的問題,答案是:

    函式,其實就是閉包的一種「特例 (special case)」。

    你可以這樣理解它們的關係:

    • 閉包 (Closure):是一個更通用、更根本的概念。它是指任何可以捕獲其環境的、可執行的程式碼區塊。
    • 函式 (Function):是一種有名字、且通常不捕獲任何周遭值的閉包

    在 Swift 中,函式與閉包都是「一級公民 (First-Class Citizens)」,代表它們可以:

    • 被指派給一個變數或常數。
    • 當作參數傳遞給另一個函式。
    • 作為一個函式的回傳值。

    關係展示

    
    // --- 1. 函式 (有名字的閉包) ---
    func double(number: Int) -> Int {
        return number * 2
    }
    
    // --- 2. 閉包 (匿名的閉包) ---
    let triple = { (number: Int) -> Int in
        return number * 3
    }
    
    // --- 3. 兩者皆可當作參數傳遞 ---
    
    // 註解:定義一個接收「(Int) -> Int」這種 "形狀" 的閉包或函式作為參數的函式
    func operate(on number: Int, by operation: (Int) -> Int) -> Int {
        return operation(number)
    }
    
    // 註解:傳入一個「函式」
    let resultFromFunction = operate(on: 10, by: double) // 結果是 20
    
    // 註解:傳入一個「閉包」
    let resultFromClosure = operate(on: 10, by: triple)  // 結果是 30
    
    // 註解:甚至可以直接在呼叫時,當場寫一個閉包傳入 (Trailing Closure 語法)
    let resultFromInlineClosure = operate(on: 10) { number in
        return number * 4
    } // 結果是 40
    
    print("函式運算結果: \(resultFromFunction)")
    print("閉包運算結果: \(resultFromClosure)")
    print("行內閉包運算結果: \(resultFromInlineClosure)")
    

    一句話總結

    閉包是一個能「捕獲」周遭環境變數的獨立程式碼區塊,而函式則是一種有名字、方便重複呼叫的閉包特例。

    • 我個人的心得是,閉包一開始要花點時間硬啃,但可以像我一樣,把官方文件當字典,把 AI 當家教,一邊問一邊學。如果能打開 Xcode 親手試試看,效果會更好!

今天真的是一場硬仗。Optional 像一個貼心的提醒,函式像一個方便的工具,而閉包...則像一門深奧的魔法,我感覺自己也才會一半而已QQ

連續兩天高強度的語法學習後,明天我們終於可以放鬆一下,從具體的程式碼中抽離,聊一些更「軟」但同樣重要的事:如何建立良好的開發習慣,並將我們的 AI 夥伴調教成一位更稱職的學習家教。


上一篇
Day 2 - 語法核心 (一):Swift 的變數、常數、流程控制與集合
下一篇
Day 4 - 開發習慣:聊聊 Coding Guidelines 與如何讓 AI 成為你的學習家教
系列文
iOS? AI-Yes!:用 Vide Coding 加速我的 Swift 學習曲線4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言