引言

Swift是苹果公司于2014年推出的编程语言,专为iOS、macOS、watchOS和tvOS开发而设计。它结合了现代编程语言的最佳特性,如安全、快速和表达力强。对于初学者来说,Swift的学习曲线相对平缓,但要真正掌握iOS开发,需要系统性的学习和大量的实战经验。本文将从零开始,分享Swift编程的实战经验,涵盖核心技巧和常见问题的解决方案,帮助你从新手成长为一名合格的iOS开发者。

第一部分:Swift基础语法与核心概念

1.1 变量与常量

在Swift中,变量使用var声明,常量使用let声明。常量一旦赋值就不能更改,这有助于编写更安全的代码。

// 变量
var greeting = "Hello, World!"
greeting = "Hello, Swift!" // 可以修改

// 常量
let name = "Alice"
// name = "Bob" // 编译错误,常量不可修改

1.2 数据类型

Swift是强类型语言,支持多种数据类型,包括整数、浮点数、字符串、布尔值等。

// 整数
let age: Int = 25

// 浮点数
let height: Double = 1.75

// 字符串
let message: String = "Welcome to Swift"

// 布尔值
let isDeveloper: Bool = true

1.3 控制流

Swift提供了丰富的控制流语句,如ifforwhileswitch

// if语句
let score = 85
if score >= 90 {
    print("优秀")
} else if score >= 60 {
    print("及格")
} else {
    print("不及格")
}

// for循环
for i in 1...5 {
    print(i)
}

// while循环
var count = 0
while count < 5 {
    print(count)
    count += 1
}

// switch语句
let number = 3
switch number {
case 1:
    print("一")
case 2:
    print("二")
case 3:
    print("三")
default:
    print("其他")
}

1.4 函数

函数是Swift中的基本构建块,用于封装可重用的代码。

// 定义函数
func greet(name: String) -> String {
    return "Hello, \(name)!"
}

// 调用函数
let greeting = greet(name: "Bob")
print(greeting) // 输出: Hello, Bob!

// 函数参数标签
func multiply(_ a: Int, by b: Int) -> Int {
    return a * b
}
let result = multiply(5, by: 3) // 调用时不需要写第一个参数标签
print(result) // 输出: 15

1.5 闭包

闭包是自包含的功能代码块,可以在代码中被传递和使用。

// 闭包表达式
let names = ["Anna", "Alex", "Brian", "Jack"]
let sortedNames = names.sorted { $0 < $1 }
print(sortedNames) // 输出: ["Alex", "Anna", "Brian", "Jack"]

// 逃逸闭包
func performAsyncOperation(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        sleep(2) // 模拟耗时操作
        completion("操作完成")
    }
}

performAsyncOperation { result in
    print(result) // 输出: 操作完成
}

1.6 结构体与类

Swift中,结构体(struct)和类(class)都是用于定义自定义数据类型,但结构体是值类型,类是引用类型。

// 结构体
struct Person {
    var name: String
    var age: Int
}

var person1 = Person(name: "Alice", age: 25)
var person2 = person1 // 值类型,复制
person2.name = "Bob"
print(person1.name) // 输出: Alice (person1未改变)

// 类
class Animal {
    var species: String
    init(species: String) {
        self.species = species
    }
}

var animal1 = Animal(species: "Dog")
var animal2 = animal1 // 引用类型,共享
animal2.species = "Cat"
print(animal1.species) // 输出: Cat (animal1也改变了)

1.7 协议

协议定义了一组方法和属性,用于定义某种功能的蓝图。

protocol Drawable {
    func draw()
}

class Circle: Drawable {
    func draw() {
        print("绘制圆形")
    }
}

let circle = Circle()
circle.draw() // 输出: 绘制圆形

第二部分:iOS开发核心技巧

2.1 界面构建:UIKit vs SwiftUI

UIKit

UIKit是传统的iOS界面框架,使用故事板(Storyboard)或纯代码构建界面。

使用故事板构建界面:

  1. 在Xcode中创建一个新项目,选择“Single View App”。
  2. 打开Main.storyboard,拖拽一个UILabel和一个UIButton到视图控制器上。
  3. 创建一个ViewController.swift文件,将故事板中的控件与代码连接。
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = "欢迎使用UIKit"
    }
    
    @IBAction func buttonTapped(_ sender: UIButton) {
        label.text = "按钮被点击了!"
    }
}

纯代码构建界面:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }
    
    func setupUI() {
        let label = UILabel()
        label.text = "纯代码构建的界面"
        label.textAlignment = .center
        label.frame = CGRect(x: 50, y: 100, width: 300, height: 40)
        view.addSubview(label)
        
        let button = UIButton(type: .system)
        button.setTitle("点击我", for: .normal)
        button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        view.addSubview(button)
    }
    
    @objc func buttonTapped() {
        print("按钮被点击了!")
    }
}

SwiftUI

SwiftUI是苹果推出的声明式UI框架,使用Swift编写,支持跨平台(iOS、macOS、watchOS、tvOS)。

import SwiftUI

struct ContentView: View {
    @State private var text = "欢迎使用SwiftUI"
    
    var body: some View {
        VStack(spacing: 20) {
            Text(text)
                .font(.title)
                .padding()
            
            Button("点击我") {
                text = "按钮被点击了!"
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
        }
    }
}

// 预览
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2.2 数据持久化

UserDefaults

UserDefaults适用于存储少量的用户偏好设置。

// 保存数据
UserDefaults.standard.set("Alice", forKey: "username")
UserDefaults.standard.set(25, forKey: "age")
UserDefaults.standard.set(true, forKey: "isLoggedIn")

// 读取数据
let username = UserDefaults.standard.string(forKey: "username") ?? "默认用户名"
let age = UserDefaults.standard.integer(forKey: "age")
let isLoggedIn = UserDefaults.standard.bool(forKey: "isLoggedIn")

print("用户名: \(username), 年龄: \(age), 登录状态: \(isLoggedIn)")

Core Data

Core Data是苹果提供的对象图管理和持久化框架,适用于复杂的数据模型。

import CoreData

// 定义数据模型
class CoreDataStack {
    static let shared = CoreDataStack()
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores { (storeDescription, error) in
            if let error = error {
                fatalError("Core Data加载失败: \(error)")
            }
        }
        return container
    }()
    
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    // 保存数据
    func saveContext() {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("保存失败: \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

// 使用示例
let coreDataStack = CoreDataStack.shared
let context = coreDataStack.context

// 创建实体
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)!
let user = NSManagedObject(entity: entity, insertInto: context)

// 设置属性
user.setValue("Alice", forKey: "name")
user.setValue(25, forKey: "age")

// 保存
coreDataStack.saveContext()

// 查询
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "User")
do {
    let users = try context.fetch(fetchRequest) as! [NSManagedObject]
    for user in users {
        if let name = user.value(forKey: "name") as? String,
           let age = user.value(forKey: "age") as? Int {
            print("用户: \(name), 年龄: \(age)")
        }
    }
} catch {
    print("查询失败: \(error)")
}

Realm

Realm是一个跨平台的移动数据库,以高性能和易用性著称。

import RealmSwift

// 定义模型
class User: Object {
    @Persisted var name: String = ""
    @Persisted var age: Int = 0
}

// 初始化Realm
let realm = try! Realm()

// 创建并保存对象
let user = User()
user.name = "Alice"
user.age = 25

try! realm.write {
    realm.add(user)
}

// 查询
let users = realm.objects(User.self)
for user in users {
    print("用户: \(user.name), 年龄: \(user.age)")
}

2.3 网络请求

URLSession

URLSession是苹果提供的网络请求框架,支持同步和异步请求。

import Foundation

// 异步请求
func fetchDataFromServer() {
    let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            print("请求失败: \(error)")
            return
        }
        
        guard let data = data else {
            print("没有数据")
            return
        }
        
        do {
            let json = try JSONSerialization.jsonObject(with: data, options: [])
            print("响应数据: \(json)")
        } catch {
            print("JSON解析失败: \(error)")
        }
    }
    task.resume()
}

// 调用
fetchDataFromServer()

Alamofire

Alamofire是一个流行的第三方网络库,简化了网络请求的编写。

import Alamofire

// GET请求
AF.request("https://jsonplaceholder.typicode.com/posts/1").responseJSON { response in
    switch response.result {
    case .success(let value):
        print("响应数据: \(value)")
    case .failure(let error):
        print("请求失败: \(error)")
    }
}

// POST请求
let parameters: [String: Any] = ["title": "foo", "body": "bar", "userId": 1]
AF.request("https://jsonplaceholder.typicode.com/posts", method: .post, parameters: parameters).responseJSON { response in
    switch response.result {
    case .success(let value):
        print("响应数据: \(value)")
    case .failure(let error):
        print("请求失败: \(error)")
    }
}

2.4 多线程与异步编程

GCD (Grand Central Dispatch)

GCD是苹果提供的多线程解决方案,用于管理并发任务。

// 主线程更新UI
DispatchQueue.main.async {
    // 更新UI代码
    self.label.text = "更新后的文本"
}

// 后台线程执行耗时任务
DispatchQueue.global().async {
    // 耗时操作,如网络请求、数据处理
    let data = self.fetchData()
    
    // 回到主线程更新UI
    DispatchQueue.main.async {
        self.updateUI(with: data)
    }
}

// 使用DispatchGroup管理多个任务
let group = DispatchGroup()
let queue = DispatchQueue.global()

group.enter()
queue.async {
    // 任务1
    print("任务1开始")
    sleep(1)
    print("任务1完成")
    group.leave()
}

group.enter()
queue.async {
    // 任务2
    print("任务2开始")
    sleep(2)
    print("任务2完成")
    group.leave()
}

group.notify(queue: .main) {
    print("所有任务完成")
}

async/await (Swift 5.5+)

Swift 5.5引入了async/await语法,使异步代码更易读。

import Foundation

// 定义异步函数
func fetchData() async throws -> Data {
    let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

// 调用异步函数
func process() async {
    do {
        let data = try await fetchData()
        let json = try JSONSerialization.jsonObject(with: data, options: [])
        print("响应数据: \(json)")
    } catch {
        print("错误: \(error)")
    }
}

// 在UI上下文中调用(需要在async上下文中)
Task {
    await process()
}

2.5 内存管理

引用计数与循环引用

Swift使用自动引用计数(ARC)管理内存。需要注意避免循环引用。

// 循环引用示例
class Person {
    var name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name)被释放")
    }
}

class Apartment {
    var unit: String
    weak var tenant: Person? // 使用weak避免循环引用
    
    init(unit: String) {
        self.unit = unit
    }
    
    deinit {
        print("公寓\(unit)被释放")
    }
}

// 使用
var alice: Person? = Person(name: "Alice")
var apartment: Apartment? = Apartment(unit: "101")

alice?.apartment = apartment
apartment?.tenant = alice

alice = nil
apartment = nil // 此时两个对象都会被释放

内存泄漏检测

使用Xcode的Instruments工具检测内存泄漏。

  1. 在Xcode中,选择Product > Profile。
  2. 选择Leaks模板。
  3. 运行应用,执行可能导致泄漏的操作。
  4. 分析Leaks工具中的泄漏报告。

第三部分:常见问题解决方案

3.1 界面布局问题

自动布局(Auto Layout)

自动布局是iOS开发中常用的布局方式,使用约束来定义视图的位置和大小。

import UIKit

class AutoLayoutViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }
    
    func setupUI() {
        let label = UILabel()
        label.text = "自动布局示例"
        label.textAlignment = .center
        label.backgroundColor = .lightGray
        label.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(label)
        
        // 设置约束
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            label.widthAnchor.constraint(equalToConstant: 200),
            label.heightAnchor.constraint(equalToConstant: 50)
        ])
    }
}

SwiftUI布局

SwiftUI使用声明式语法,布局更简单。

import SwiftUI

struct LayoutExampleView: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("SwiftUI布局示例")
                .font(.title)
                .frame(maxWidth: .infinity)
                .background(Color.blue)
                .foregroundColor(.white)
            
            HStack(spacing: 10) {
                ForEach(1...3, id: \.self) { index in
                    Text("按钮\(index)")
                        .padding()
                        .background(Color.green)
                        .cornerRadius(8)
                }
            }
        }
        .padding()
    }
}

3.2 数据传递问题

闭包传值

闭包是iOS开发中常用的数据传递方式。

// 定义闭包
typealias CompletionHandler = (String) -> Void

class FirstViewController: UIViewController {
    var completionHandler: CompletionHandler?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(type: .system)
        button.setTitle("跳转到第二页", for: .normal)
        button.addTarget(self, action: #selector(goToSecondVC), for: .touchUpInside)
        view.addSubview(button)
    }
    
    @objc func goToSecondVC() {
        let secondVC = SecondViewController()
        secondVC.completionHandler = { result in
            print("从第二页返回的数据: \(result)")
            // 更新UI等操作
        }
        present(secondVC, animated: true)
    }
}

class SecondViewController: UIViewController {
    var completionHandler: CompletionHandler?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        let button = UIButton(type: .system)
        button.setTitle("返回数据", for: .normal)
        button.addTarget(self, action: #selector(returnData), for: .touchUpInside)
        button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
        view.addSubview(button)
    }
    
    @objc func returnData() {
        completionHandler?("这是从第二页返回的数据")
        dismiss(animated: true)
    }
}

通知(NotificationCenter)

通知用于跨页面或跨对象的数据传递。

// 发送通知
NotificationCenter.default.post(name: Notification.Name("DataUpdated"), object: nil, userInfo: ["data": "新数据"])

// 接收通知
NotificationCenter.default.addObserver(self, selector: #selector(handleDataUpdate), name: Notification.Name("DataUpdated"), object: nil)

@objc func handleDataUpdate(notification: Notification) {
    if let data = notification.userInfo?["data"] as? String {
        print("收到通知: \(data)")
    }
}

// 移除通知(在deinit中)
deinit {
    NotificationCenter.default.removeObserver(self)
}

3.3 性能优化

列表视图优化(UITableView/UICollectionView)

对于长列表,使用复用机制和异步加载。

// UITableView复用机制
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    // 配置cell
    return cell
}

// 异步加载图片
func loadImageAsync(url: URL, completion: @escaping (UIImage?) -> Void) {
    DispatchQueue.global().async {
        if let data = try? Data(contentsOf: url) {
            let image = UIImage(data: data)
            DispatchQueue.main.async {
                completion(image)
            }
        } else {
            DispatchQueue.main.async {
                completion(nil)
            }
        }
    }
}

图片缓存

使用NSCache或第三方库(如Kingfisher)进行图片缓存。

import UIKit

// 简单的图片缓存
class ImageCache {
    static let shared = ImageCache()
    private let cache = NSCache<NSString, UIImage>()
    
    func setImage(_ image: UIImage, forKey key: String) {
        cache.setObject(image, forKey: key as NSString)
    }
    
    func image(forKey key: String) -> UIImage? {
        return cache.object(forKey: key as NSString)
    }
}

// 使用示例
let url = URL(string: "https://example.com/image.jpg")!
if let cachedImage = ImageCache.shared.image(forKey: url.absoluteString) {
    // 使用缓存图片
    imageView.image = cachedImage
} else {
    // 下载图片并缓存
    loadImageAsync(url: url) { image in
        if let image = image {
            ImageCache.shared.setImage(image, forKey: url.absoluteString)
            DispatchQueue.main.async {
                imageView.image = image
            }
        }
    }
}

3.4 错误处理

do-catch错误处理

Swift使用do-catch处理运行时错误。

func readFile(at path: String) throws -> String {
    let data = try Data(contentsOf: URL(fileURLWithPath: path))
    return String(data: data, encoding: .utf8) ?? ""
}

do {
    let content = try readFile(at: "/path/to/file.txt")
    print("文件内容: \(content)")
} catch {
    print("读取文件失败: \(error)")
}

可选类型与强制解包

避免使用强制解包(!),使用可选绑定或??提供默认值。

// 不好的做法
let optionalString: String? = nil
// let unwrapped = optionalString! // 可能导致崩溃

// 好的做法
if let unwrapped = optionalString {
    print("有值: \(unwrapped)")
} else {
    print("值为nil")
}

// 使用??提供默认值
let safeString = optionalString ?? "默认值"
print(safeString) // 输出: 默认值

3.5 跨平台开发

SwiftUI跨平台

SwiftUI支持iOS、macOS、watchOS和tvOS,可以编写一次代码,多平台运行。

import SwiftUI

struct CrossPlatformView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .font(.title)
                .padding()
            
            Button("Click Me") {
                print("Button tapped")
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
        }
        #if os(macOS)
        .frame(width: 400, height: 300)
        #endif
    }
}

第四部分:实战项目示例

4.1 项目一:天气应用

项目概述

创建一个简单的天气应用,使用OpenWeatherMap API获取天气数据。

步骤

  1. 创建项目:在Xcode中创建一个新项目,选择“Single View App”。
  2. 界面设计:使用UIKit或SwiftUI构建界面。
  3. 网络请求:使用URLSession或Alamofire获取天气数据。
  4. 数据解析:使用Codable解析JSON数据。
  5. 显示数据:将天气数据展示在界面上。

代码示例(使用SwiftUI)

import SwiftUI

// 数据模型
struct WeatherResponse: Codable {
    let main: Main
    let weather: [Weather]
    let name: String
}

struct Main: Codable {
    let temp: Double
    let humidity: Int
}

struct Weather: Codable {
    let description: String
}

// 视图模型
class WeatherViewModel: ObservableObject {
    @Published var weather: WeatherResponse?
    @Published var isLoading = false
    @Published var error: String?
    
    func fetchWeather(city: String) {
        isLoading = true
        error = nil
        
        let apiKey = "YOUR_API_KEY"
        let urlString = "https://api.openweathermap.org/data/2.5/weather?q=\(city)&appid=\(apiKey)&units=metric"
        
        guard let url = URL(string: urlString) else {
            self.error = "无效的URL"
            self.isLoading = false
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            DispatchQueue.main.async {
                self.isLoading = false
                
                if let error = error {
                    self.error = error.localizedDescription
                    return
                }
                
                guard let data = data else {
                    self.error = "没有数据"
                    return
                }
                
                do {
                    let decoder = JSONDecoder()
                    let weatherResponse = try decoder.decode(WeatherResponse.self, from: data)
                    self.weather = weatherResponse
                } catch {
                    self.error = "解析失败: \(error.localizedDescription)"
                }
            }
        }.resume()
    }
}

// 视图
struct WeatherView: View {
    @StateObject private var viewModel = WeatherViewModel()
    @State private var city = "Beijing"
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                TextField("输入城市", text: $city)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()
                
                Button("获取天气") {
                    viewModel.fetchWeather(city: city)
                }
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
                
                if viewModel.isLoading {
                    ProgressView()
                } else if let error = viewModel.error {
                    Text("错误: \(error)")
                        .foregroundColor(.red)
                } else if let weather = viewModel.weather {
                    VStack {
                        Text(weather.name)
                            .font(.title)
                        Text("温度: \(Int(weather.main.temp))°C")
                            .font(.headline)
                        Text("湿度: \(weather.main.humidity)%")
                        Text("天气: \(weather.weather.first?.description ?? "未知")")
                    }
                    .padding()
                    .background(Color.gray.opacity(0.2))
                    .cornerRadius(10)
                }
                
                Spacer()
            }
            .navigationTitle("天气应用")
        }
    }
}

4.2 项目二:待办事项应用

项目概述

创建一个待办事项应用,支持添加、删除和标记完成。

步骤

  1. 创建项目:在Xcode中创建一个新项目,选择“Single View App”。
  2. 数据模型:定义待办事项的数据结构。
  3. 数据持久化:使用Core Data或Realm存储数据。
  4. 界面设计:使用UITableView或List显示待办事项。
  5. 交互功能:实现添加、删除和标记完成的功能。

代码示例(使用UIKit和Core Data)

import UIKit
import CoreData

// 数据模型
class TodoItem: NSManagedObject {
    @NSManaged var title: String
    @NSManaged var isCompleted: Bool
    @NSManaged var createdAt: Date
}

// 视图控制器
class TodoListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    var tableView: UITableView!
    var todoItems: [TodoItem] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadTodoItems()
    }
    
    func setupUI() {
        title = "待办事项"
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTodoItem))
        
        tableView = UITableView(frame: view.bounds, style: .plain)
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        view.addSubview(tableView)
    }
    
    func loadTodoItems() {
        let context = CoreDataStack.shared.context
        let fetchRequest: NSFetchRequest<TodoItem> = TodoItem.fetchRequest()
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
        
        do {
            todoItems = try context.fetch(fetchRequest)
            tableView.reloadData()
        } catch {
            print("加载失败: \(error)")
        }
    }
    
    @objc func addTodoItem() {
        let alert = UIAlertController(title: "添加待办事项", message: nil, preferredStyle: .alert)
        alert.addTextField { textField in
            textField.placeholder = "输入事项"
        }
        
        let addAction = UIAlertAction(title: "添加", style: .default) { _ in
            guard let text = alert.textFields?.first?.text, !text.isEmpty else { return }
            
            let context = CoreDataStack.shared.context
            let todoItem = TodoItem(context: context)
            todoItem.title = text
            todoItem.isCompleted = false
            todoItem.createdAt = Date()
            
            CoreDataStack.shared.saveContext()
            self.loadTodoItems()
        }
        
        let cancelAction = UIAlertAction(title: "取消", style: .cancel)
        
        alert.addAction(addAction)
        alert.addAction(cancelAction)
        
        present(alert, animated: true)
    }
    
    // MARK: - UITableViewDataSource
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return todoItems.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let todoItem = todoItems[indexPath.row]
        
        cell.textLabel?.text = todoItem.title
        cell.accessoryType = todoItem.isCompleted ? .checkmark : .none
        
        return cell
    }
    
    // MARK: - UITableViewDelegate
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        
        let todoItem = todoItems[indexPath.row]
        todoItem.isCompleted.toggle()
        
        CoreDataStack.shared.saveContext()
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            let todoItem = todoItems[indexPath.row]
            let context = CoreDataStack.shared.context
            context.delete(todoItem)
            
            CoreDataStack.shared.saveContext()
            todoItems.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
        }
    }
}

第五部分:学习资源与进阶建议

5.1 学习资源

  1. 官方文档:Apple Developer Documentation(developer.apple.com)
  2. 在线课程:Udemy、Coursera、Ray Wenderlich
  3. 书籍:《Swift编程语言》、《iOS编程》、《SwiftUI实战》
  4. 社区:Stack Overflow、Reddit(r/swift)、Swift Forums

5.2 进阶建议

  1. 深入学习Swift:掌握高级特性,如泛型、协议、扩展、内存管理等。
  2. 学习设计模式:MVC、MVVM、VIPER等架构模式。
  3. 参与开源项目:在GitHub上贡献代码,学习他人代码。
  4. 构建个人项目:从简单应用开始,逐步增加复杂度。
  5. 关注技术动态:关注WWDC、Swift博客、技术社区。

结语

Swift编程和iOS开发是一个不断学习和实践的过程。通过掌握基础语法、核心技巧和常见问题的解决方案,你可以逐步构建出高质量的iOS应用。记住,实践是最好的老师,多写代码、多做项目,才能真正掌握这些知识。希望本文能为你提供有价值的指导,祝你在iOS开发的道路上越走越远!