Swift Combine 学习(七):实践应用场景举例

引言

在前面的系列文章中,我们已经深入学习了 Combine 框架的各个组成部分和使用方法。现在,是时候将这些理论知识付诸实践了。本文将通过实际的编程案例,展示 Combine 在日常开发中的应用场景,包括网络请求处理、用户界面交互、数据绑定等。通过这些实例,希望能够帮助您更好地理解如何在实际项目中使用 Combine。

Combine 应用

网络请求处理

Combine 非常适合处理网络请求。以下是一个使用 Combine 进行网络请求的示例:

  1. NetworkService 类定义了一个 fetchPosts() 方法,返回一个 AnyPublisher<[Post], Error>
  2. 使用 URLSession.shared.dataTaskPublisher 创建网络请求。
  3. 使用 map 操作符提取响应数据。
  4. 使用 decode 操作符将 JSON 数据解码为 [Post] 数组。
  5. 使用 @Published 属性包装器声明 posts 数组,当它的值改变时会自动通知。
  6. fetchPosts() 方法订阅网络请求的结果。
import UIKit
import Combine

enum NetworkError: Error {
    case invalidURL
    case requestFailed
    case decodingFailed
}

struct Post: Codable {
    let id: Int
    let title: String
    let body: String
}

class NetworkService {
    func fetchPosts() -> AnyPublisher<[Post], Error> {
        // 使用 JSONPlaceholder 提供的测试 API
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
            return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher()
        }
        
        return URLSession.shared.dataTaskPublisher(for: url)
            .tryMap { output in
                guard let response = output.response as? HTTPURLResponse,
                      response.statusCode == 200 else {
                    throw NetworkError.requestFailed
                }
                return output.data
            }
            .decode(type: [Post].self, decoder: JSONDecoder())
            .mapError { error -> Error in
                switch error {
                case is URLError:
                    return NetworkError.requestFailed
                case is DecodingError:
                    return NetworkError.decodingFailed
                default:
                    return error
                }
            }
            .eraseToAnyPublisher()
    }
}

class PostsViewModel: ObservableObject {
    @Published var posts: [Post] = []
    private var cancellables = Set<AnyCancellable>()
    private let networkService = NetworkService()
    
    func fetchPosts() {
        print("   开始获取帖子...")
        
        networkService.fetchPosts()
            .receive(on: DispatchQueue.main)
            .sink { completion in
                switch completion {
                case .finished:
                    print("✅ 成功获取帖子")
                case .failure(let error):
                    print("❌ 获取帖子失败: \(error)")
                }
            } receiveValue: { [weak self] posts in
                self?.posts = posts
                print("📝 获取到 \(posts.count) 条帖子")
                // 打印前2条帖子的标题
                posts.prefix(2).forEach { post in
                    print("   标题: \(post.title)")
                }
            }
            .store(in: &cancellables)
    }
}

// test
func testNetworkRequest() {
    let viewModel = PostsViewModel()
    viewModel.fetchPosts()
    RunLoop.main.run(until: Date(timeIntervalSinceNow: 5))
}

print("🍎开始网络请求测试")
testNetworkRequest()

/*输出:
🍎 开始网络请求测试
   开始获取帖子...
📝 获取到 100 条帖子
   标题: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
   标题: qui est esse
✅ 成功获取帖子
*/

请求链式调用

有时我们需要基于第一个请求的结果发起第二个请求:

import UIKit
import Combine
import Foundation
import SwiftUI

struct User: Codable {
    let id: Int
    let name: String
}

struct UserPosts: Codable {
    let user: User
    let posts: [Post]
}

enum NetworkError: Error {
    case invalidURL
    case requestFailed
    case decodingFailed
}

struct Post: Codable {
    let id: Int
    let title: String
    let body: String
}

class NetworkService {
    func fetchPosts() -> AnyPublisher<[Post], Error> {
        // 使用 JSONPlaceholder 提供的测试 API
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
            return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher()
        }
        
        return URLSession.shared.dataTaskPublisher(for: url)
            .tryMap { output in
                guard let response = output.response as? HTTPURLResponse,
                      response.statusCode == 200 else {
                    throw NetworkError.requestFailed
                }
                return output.data
            }
            .decode(type: [Post].self, decoder: JSONDecoder())
            .mapError { error -> Error in
                switch error {
                case is URLError:
                    return NetworkError.requestFailed
                case is DecodingError:
                    return NetworkError.decodingFailed
                default:
                    return error
                }
            }
            .eraseToAnyPublisher()
    }
}

extension NetworkService {
    // fetch user info
    func fetchUser(id: Int) -> AnyPublisher<User, Error> {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/users/\(id)") else {
            return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher()
        }
        
        return URLSession.shared.dataTaskPublisher(for: url)
            .tryMap { output in
                guard let response = output.response as? HTTPURLResponse,
                      response.statusCode == 200 else {
                    throw NetworkError.requestFailed
                }
                return output.data
            }
            .decode(type: User.self, decoder: JSONDecoder())
            .mapError { error -> Error in
                switch error {
                case is URLError:
                    return NetworkError.requestFailed
                case is DecodingError:
                    return NetworkError.decodingFailed
                default:
                    return error
                }
            }
            .eraseToAnyPublisher()
    }
    
    // fetch user post
    func fetchPosts(userID: Int) -> AnyPublisher<[Post], Error> {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/users/\(userID)/posts") else {
            return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher()
        }
        
        return URLSession.shared.dataTaskPublisher(for: url)
            .tryMap { output in
                guard let response = output.response as? HTTPURLResponse,
                      response.statusCode == 200 else {
                    throw NetworkError.requestFailed
                }
                return output.data
            }
            .decode(type: [Post].self, decoder: JSONDecoder())
            .mapError { error -> Error in
                switch error {
                case is URLError:
                    return NetworkError.requestFailed
                case is DecodingError:
                    return NetworkError.decodingFailed
                default:
                    return error
                }
            }
            .eraseToAnyPublisher()
    }
    
    // 组合请求:fetch user info and posts
    func fetchUserAndPosts(userID: Int) -> AnyPublisher<UserPosts, Error> {
        fetchUser(id: userID)
            .flatMap { user -> AnyPublisher<UserPosts, Error> in
                self.fetchPosts(userID: user.id)
                    .map { posts in
                        UserPosts(user: user, posts: posts)
                    }
                    .eraseToAnyPublisher()
            }
            .eraseToAnyPublisher()
    }
}

// ViewModel
class UserPostsViewModel: ObservableObject {
    @Published var userPosts: UserPosts?
    private var cancellables = Set<AnyCancellable>()
    private let networkService = NetworkService()
    
    func fetchUserAndPosts(userID: Int) {
        print("   开始获取用户 \(userID) 的信息和帖子...")
        
        networkService.fetchUserAndPosts(userID: userID)
            .receive(on: DispatchQueue.main)
            .sink { completion in
                switch completion {
                case .finished:
                    print("✅ 成功获取用户信息和帖子")
                case .failure(let error):
                    print("❌ 获取失败: \(error)")
                }
            } receiveValue: { [weak self] userPosts in
                self?.userPosts = userPosts
                print("\n📝 用户信息:")
                print("   ID: \(userPosts.user.id)")
                print("   名字: \(userPosts.user.name)")
                print("\n📝 该用户的帖子(前2条):")
                userPosts.posts.prefix(2).forEach { post in
                    print("   标题: \(post.title)")
                }
            }
            .store(in: &cancellables)
    }
}

print("🍎开始组合请求测试")
testUserAndPosts()

/*输出
🍎开始组合请求测试
  开始获取用户 1 的信息和帖子...

📝 用户信息:
   ID: 1
   名字: Leanne Graham

📝 该用户的帖子(前2条):
   标题: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
   标题: qui est esse
✅ 成功获取用户信息和帖子
*/

并发请求

当需要同时发起多个请求并等待所有结果时:

func fetchMultipleUsers(ids: [Int]) -> AnyPublisher<[User], Error> {
    let publishers = ids.map { fetchUser(id: $0) }
    
    return Publishers.MergeMany(publishers)
        .collect()
        .eraseToAnyPublisher()
}

func fetchUserAndProfile(userID: Int) -> AnyPublisher<(User, Profile), Error> {
  // 使用 zip 保顺序
    Publishers.Zip(
        fetchUser(id: userID),
        fetchProfile(userID: userID)
    )
    .eraseToAnyPublisher()
}

用户界面更新

Combine 还可以用于响应式地更新用户界面。以下是一个简单的示例,展示如何使用 Combine 更新 UIKit 界面:

  1. 创建一个 UISearchBar 扩展,增加一个 textDidChangePublisher 属性。
extension UISearchBar {
    var textDidChangePublisher: AnyPublisher<String, Never> {
        NotificationCenter.default.publisher(for: UISearchBar.textDidChangeNotification, object: self)
            .compactMap { $0.object as? UISearchBar }
            .map { $0.text ?? "" }
            .eraseToAnyPublisher()
    }
}

class SearchViewModel {
    @Published private(set) var searchResults: [String] = []
    
    func search(query: String) {
        // 模拟网络请求
        DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
            let results = (0..<10).map { "\(query) result \($0)" }
            self.searchResults = results
        }
    }
}
  1. setupBindings() 方法设置了两个主要的数据流:

    1. 搜索栏文本变化到搜索操作。
    2. 搜索结果到 UI 更新。
    class SearchViewController: UIViewController {
        // ... 声明一个 searchBar 、一个 tableview
        
        private var viewModel = SearchViewModel()
        private var cancellables = Set<AnyCancellable>()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            setupBindings()
        }
        
        private func setupBindings() {
            searchBar.textDidChangePublisher
                .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
                .removeDuplicates()
                .sink { [weak self] searchText in
                    self?.viewModel.search(query: searchText)
                }
                .store(in: &cancellables)
            
            viewModel.$searchResults
                .receive(on: DispatchQueue.main)
                .sink { [weak self] _ in
                    self?.tableView.reloadData()
                }
                .store(in: &cancellables)
        }
    }
    
  2. 使用 @Published 属性包装器声明 searchResults,允许外部订阅,但只允许内部修改。search(query:) 方法模拟一个异步网络请求。

    class SearchViewModel {
        @Published private(set) var searchResults: [String] = []
        
        func search(query: String) {
            DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
                let results = (0..<10).map { "\(query) result \($0)" }
                self.searchResults = results
            }
        }
    }
    

数据绑定

Combine 非常适合用于数据绑定,特别是在 MVVM 架构中。以下是一个简单的例子:

  1. User 类使用@Published 属性包装。
  2. 使用 Publishers.CombineLatest 来响应任一属性的变化。
  3. UserVC 订阅 ViewModel 的 displayName 属性,并更新 UI。
class User {
    @Published var name: String
    @Published var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

class UserViewModel {
    @Published private(set) var displayName: String = ""
    private var user: User
    private var cancellables = Set<AnyCancellable>()
    
    init(user: User) {
        self.user = user
        setupBindings()
    }
    
    private func setupBindings() {
        Publishers.CombineLatest($user.name, $user.age)
            .map { name, age in
                return "\(name) (\(age) years old)"
            }
            .assign(to: \.displayName, on: self)
            .store(in: &cancellables)
    }
}

class UserVC: UIViewController {
	// ... 声明一个 nameLabel
    
    private var viewModel: UserViewModel!
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let user = User(name: "Joy", age: 91)
        viewModel = UserViewModel(user: user)
        
        viewModel.$displayName
            .receive(on: DispatchQueue.main)
            .sink { [weak self] displayName in
                self?.nameLabel.text = displayName
            }
            .store(in: &cancellables)
    }
}

Combine VS RxSwift

RxSwift 是 ReactiveX 中的一个。ReactiveX 还有 RxJava、RxJS、RxKotlin 等等。以下是一个简单的对照表:

RxSwiftCombine说明
ObservablePublisher发送数据的源
ObserverSubscriber接收数据的目标
DisposeBagSet管理订阅生命周期
SubjectSubject既可发送也可接收数据

以下是一些常用操作符的对照表:

RxSwiftCombine说明
mapmap值转换
flatMapflatMap转换为新的 Observable/Publisher
filterfilter过滤值
distinctUntilChangedremoveDuplicates去重
debouncedebounce防抖
throttlethrottle节流
mergemerge合并数据流
combineLatestcombineLatest组合最新值
zipzip配对组合

虽然 Combine 和 RxSwift 都是响应式编程框架,但它们有很多不同之处。如:

  1. 来源:

    • Combine 是 Apple 官方框架,内置于 Swift 和 iOS 13 及以上版本中。
    • RxSwift 是社区驱动的项目,适用于 iOS、macOS 和其他平台
  2. 语法基本概念相似,具体的 API 和命名有所不同。

  3. Combine 虽然内置了很多操作符,但还是比 Rxswift 少。Combine 和 RxSwift 的操作符对比 RxSwift to Combine Cheatsheet

  4. 功能集:

    • RxSwift 提供了更丰富的操作符,涵盖了更多场景。
    • Combine 的功能相对较少,但 API 设计更强调安全性和清晰的错误处理。
  5. 平台支持:

    • Combine 仅支持 Apple 平台(iOS 13+、macOS 10.15+、tvOS 13+、watchOS 6+)。
    • RxSwift 支持更广泛的平台和 iOS 版本。
  6. 学习曲线:

    • Combine 的学习曲线相对较缓,特别是对于已经熟悉 Swift 的开发者。
    • RxSwift 的学习曲线可能更陡峭,因为它包含了更多的概念和操作符。
  7. 性能

    RxSwift 是纯 Swift 实现,Combine 实际使用性能会更优。

  8. 版本支持

    Combine 支持的最低系统版本是 iOS 13。但是有开源的 OpenCombine 可以支持到 iOS 9。

以下是一个简单的对比示例:

1// Combine
let combinePublisher = (1...5).publisher
    .map { $0 * 2 }
    .filter { $0 > 5 }
    .sink { value in
        print("Combine: \(value)")
    }

// RxSwift
let rxObservable = Observable.from(1...5)
    .map { $0 * 2 }
    .filter { $0 > 5 }
    .subscribe(onNext: { value in
        print("RxSwift: \(value)")
    })
2
let disposeBag = DisposeBag()

let observable = Observable.of(1, 2, 3, 4, 5, 6)

// 用 RxSwift 的操作符进行过滤
observable
    .filter { $0 % 2 == 0 } // 只保留偶数
    .subscribe(onNext: { value in
        print("RxSwift - Even number: \(value)")
    })
    .disposed(by: disposeBag)

let publisher = [1, 2, 3, 4, 5, 6].publisher

// 用 Combine 的操作符进行过滤
let cancellable = publisher
    .filter { $0 % 2 == 0 } 
    .sink(receiveValue: { value in
        print("Combine - Even number: \(value)")
    })

虽然语法略有不同,基本概念和操作是相似的。

实践中注意点

在使用 Combine 时,以下是一些性能优化、实践、常见错误:

  1. 合理使用调度器

    使用 receive(on:) 确保在正确的线程上执行操作。

    somePublisher
        .receive(on: DispatchQueue.main)
        .sink { ... }
    
  2. 管理订阅生命周期

    始终存储和管理 AnyCancellable 对象,以确保在不再需要时取消订阅,防止内存泄漏,降低开销等。

    let publisher = [1, 2, 3, 4, 5].publisher
    
    var cancellable: AnyCancellable?
    
    func subscribeToPublisher() {
        cancellable = publisher
            .filter { $0 % 2 == 0 } 
            .sink(receiveValue: { value in
                print("Received value: \(value)")
            })
    }
    
    // 不再需要时,及时取消订阅
    func cancelSubscription() {
        cancellable?.cancel()
    }
    
  3. 共享昂贵资源

    使用 shareReplay 来共享昂贵的操作结果。如网络请求或复杂计算,这样可以避免重复执行相同的操作,从而提高性能。

    let sharedPublisher = someExpensivePublisher
        .shareReplay(1)
        .eraseToAnyPublisher()
    
  4. 优化加载体验

    使用 prependappend 优化加载体验,以提供更流畅的用户体验。比如先显示缓存数据,然后更新为最新数据,减少用户的等待时间。

    let dataPublisher = loadDataFromCache()
        .append(loadDataFromNetwork())
    
  5. 避免内存泄漏

    在闭包中使用 [weak self] 来避免循环引用。

    somePublisher
        .sink { [weak self] value in
            self?.balabala(value)
        }
        .store(in: &cancellables)
    
  6. Future 和 Just 这俩 Publisher 在初始化完成后会立即执行闭包里的逻辑,这就可能会造成不符合预期的执行流程错误。

    let IOHeavyTask = Future<Int, Never> { promise in
        print("开始耗时计算...")
        // 模拟耗时操作
        Thread.sleep(forTimeInterval: 2)
        let result = 42
        print("计算完成")
        promise(.success(result))
    }
    
    Thread.sleep(forTimeInterval: 1)
    
    print("准备订阅")
    let cancellable = IOHeavyTask.sink { value in
        print("收到结果: \(value)")
    }
    
    /*
    开始耗时计算...
    计算完成
    准备订阅
    收到结果: 42
    */
    

    在上面例子中,Future 在创建时就立即开始了耗时计算。在我们准备好订阅之前,计算就已经完成了。订阅者可能错过了计算过程,只能接收到最终结果。我们可以使用 Defferred,套在 Publisher 的外边,Deferred 允许延迟 Publisher 的创建,直到有订阅者订阅时才开始。

    let IOHeavyTask = Deferred {
        Future<Int, Never> { promise in
            print("开始耗时计算...")
            // 模拟耗时操作
            Thread.sleep(forTimeInterval: 2)
            let result = 42
            print("计算完成")
            promise(.success(result))
        }
    }
    
    Thread.sleep(forTimeInterval: 1)
    
    print("准备订阅")
    let cancellable = IOHeavyTask.sink { value in
        print("收到结果: \(value)")
    }
    
    /*
    准备订阅
    开始耗时计算...
    计算完成
    收到结果: 42
    */
    
  7. 有时候一些错误导致 Subscription 意外结束

    这里就不再举例了,简单说一下可以用 catch 来提供一个默认值或是替代的 Publisher 等等。

结语

简单说下关于 Combine 的个人愚见:

  1. 实用性:Combine 确实提供了强大的工具来处理异步编程和事件流。建议逐步将 Combine 整合到项目中,从简单的用例开始,如网络请求处理或简单的 UI 更新。这样可以在实践中学习,同时避免在整个项目中过度使用导致的复杂性。
  2. 性能 :在大多数情况下,Combine 的性能表现良好。不过在处理大量高频事件时,得注意内存使用和 CPU 占用。使用诸如 debouncethrottle 等操作符可以有效控制事件流,提高应用性能。
  3. 未来 :随着 App 开发 iOS 最低兼容将要来到 iOS 13,SwiftUI 普及,Combine 在 iOS 开发中的重要性可能会随之进一步提升。需要持续关注 WWDC 和 Apple 的文档更新。

保持务实,根据项目需求和团队能力来决定使用的程度。

<think>好的,我现在要帮用户介绍Swift的高频面试考点。首先,我需要回忆自己之前准备的Swift知识点,确保覆盖全面。用户可能正在准备面试,所以需要结构清晰、重点突出。 首先,Swift的特性是基础,比如可选类型、类型推断这些基本概念,几乎每个面试都会问到。可选类型是Swift的一大特点,要解释清楚可选绑定、强制解包和隐式解包的区别,以及为什么使用可选类型来增强安全性。 然后是值类型和引用类型的区别,特别是结构体和类的对比。这里需要提到内存管理,比如结构体在栈上的分配,类在堆上的分配,以及各自的适用场景。可能还需要涉及深拷贝和浅拷贝的问题。 协议和面向协议编程也是一个重点。要说明协议如何用于实现多态,和抽象类的区别,以及Swift标准库中协议的应用,比如Equatable、Codable等。面向协议编程的优势,比如组合优于继承,可能需要举例说明。 内存管理部分,ARC肯定是必问的。要解释ARC如何工作,强引用循环的问题,以及weak和unowned的使用场景和区别。循环引用的例子,比如闭包中捕获self,或者两个类实例相互引用,需要详细说明。 函数式编程方面,高阶函数如map、filter、reduce的使用,以及Swift中对函数式编程的支持,比如闭包作为一等公民。这里可以对比传统循环和函数式方法的优缺点。 错误处理机制,do-try-catch、throws、rethrows、defer等关键字的作用和使用场景。可能需要举例说明如何自定义错误类型,以及错误传递的过程。 泛型和关联类型在协议中的应用,这部分可能比较难,需要简明扼要地解释泛型如何提高代码复用和类型安全,以及关联类型在协议中的使用,比如在Collection协议中的Element。 访问控制的关键字,如open、public、internal、fileprivate、private,它们的作用域和区别。特别是open和public的不同,这在模块化开发中很重要。 SwiftUI和Combine框架,虽然可能根据职位需求不同,但作为现代Swift的特性,还是需要简要提及,特别是声明式UI和响应式编程的概念。 性能优化方面,结构体与类的选择、减少动态派发、使用final和private关键字优化编译等。这些点能展示对Swift底层机制的理解。 可能还需要包括一些实际编码题,比如实现单例模式、算法题等,但用户的问题主要是考点介绍,所以这部分可能放在最后作为补充。 要确保每个考点都有明确的解释,避免过于冗长,但关键点要清晰。比如在ARC部分,除了解释概念,还要给出循环引用的例子和解决方案。同时,注意使用正确的术语,比如值类型的写时复制(Copy-on-Write)机制,这也是常考的点。 另外,Swift的并发编程模型,比如async/await,可能在较新的面试中出现,需要提到Actor和主线程安全的问题,但可能属于进阶内容,根据情况决定是否包含。 最后,检查是否有遗漏的重要考点,比如属性观察器(willSet/didSet)、扩展(extension)、类型转换(as、as?、as!)、字符串处理、集合类型的高效操作等。这些也是常见问题,可能需要简要提及。 总结下来,结构应该按重要性和频率排序,每个考点用简洁的语言解释,并给出示例或注意事项。确保内容准确,覆盖全面,同时符合用户需要的深度,帮助他们在面试中有效准备。</think>以下是Swift高频面试考点的系统整理(按重要性和出现频率排序): 📌 一、Swift核心特性 1️⃣ 可选类型(Optional) - 强制解包(!)与安全解包(if let/guard let) - 可选链式调用(optional chaining) - 空合并运算符(??) - 隐式解包可选类型(Implicitly Unwrapped Optionals) 2️⃣ 值类型 vs 引用类型 - 结构体(值类型)与类(引用类型)的对比 - 写时复制(Copy-on-Write)机制 - inout关键字的作用原理 3️⃣ 协议与面向协议编程(POP) - 协议扩展(Protocol Extension) - 关联类型(associatedtype) - 协议组合(Protocol Composition) - 动态派发与静态派发机制 📌 二、内存管理 1️⃣ ARC机制 - 强/弱/无主引用(strong/weak/unowned) - 闭包的捕获列表(Capture List) - 循环引用场景分析(类与闭包的相互持有) 2️⃣ 内存安全 - 独占访问(Exclusive Access)原则 - inout参数的临时访问限制 📌 三、高级特性 1️⃣ 泛型编程 - 泛型函数与泛型类型 - 类型约束(where语句) - 协议关联类型的泛型应用 2️⃣ 错误处理 - do-catch的嵌套使用 - throws与rethrows的区别 - Result类型的使用场景 3️⃣ 模式匹配 - switch语句的完备性检查 - 元组模式匹配 - 值绑定模式(value-binding pattern) 📌 四、并发编程(Swift 5.5+) 1️⃣ async/await机制 - Task的创建与管理 - 结构化并发(Structured Concurrency) - Actor类型与数据隔离 📌 五、性能优化 1️⃣ 方法派发机制 - 静态派发(final/private) - 动态派发(继承/协议) - @objc与dynamic关键字的代价 2️⃣ 集合优化 - ContiguousArray的使用场景 - 避免隐式桥接到NS类型 - 切片(Slice)的内存特性 📌 六、实战高频问题 1️⃣ 手写单例模式(线程安全) ```swift class Singleton { static let shared = Singleton() private init() {} } ``` 2️⃣ 实现LRU缓存 ```swift class LRUCache<Key: Hashable, Value> { private let capacity: Int private var list = [Key]() private var cache = [Key: Value]() init(capacity: Int) { self.capacity = capacity } func get(_ key: Key) -> Value? { guard let value = cache[key] else { return nil } list.removeAll { $0 == key } list.append(key) return value } func set(_ key: Key, value: Value) { if cache[key] != nil { list.removeAll { $0 == key } } else if list.count >= capacity { let removedKey = list.removeFirst() cache[removedKey] = nil } list.append(key) cache[key] = value } } ``` 3️⃣ 算法实现 - 反转链表(同时要求递归/迭代两种实现) - 二叉树层序遍历(使用队列实现) - 快速排序(利用泛型实现) 📝 建议准备策略: 1. 理解Swift设计哲学(安全/高效/表达性) 2. 掌握ARC内存管理全生命周期 3. 熟练使用泛型+协议组合开发 4. 实践常见设计模式的Swift实现 5. 熟悉Swift标准库核心组件(Collection/Sequence) 通过掌握这些核心知识点,并结合实际编码练习,能有效应对90%以上的Swift技术面试考核。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值