跳到主要内容

高级格式指南

我们将介绍 SideStore 项目中的高级格式化场景、架构模式以及复杂的 Swift/Objective-C 特性。

高级 Swift 模式

协议导向编程

协议定义

  • 保持协议专注和内聚
  • 为泛型协议使用关联类型
  • 在扩展中提供适当的默认实现
// ✅ Good
protocol AppInstalling {
associatedtype AppType: App

func install(_ app: AppType) async throws
func uninstall(_ app: AppType) async throws
}

extension AppInstalling {
func validateApp(_ app: AppType) -> Bool {
return !app.identifier.isEmpty && app.version.isValid
}
}

// ❌ Bad
protocol AppManager {
func install(_ app: Any) -> Bool
func uninstall(_ app: Any) -> Bool
func update(_ app: Any) -> Bool
func backup(_ app: Any) -> Bool
func restore(_ app: Any) -> Bool
// 指责过多
}

协议组合

  • 为复杂需求使用协议组合
  • 保持单个协议小巧和专注
// ✅ Good
protocol Downloadable {
var downloadURL: URL { get }
func download() async throws -> Data
}

protocol Installable {
var installationRequirements: InstallationRequirements { get }
func install() async throws
}

typealias DeployableApp = App & Downloadable & Installable

class AppDeployer {
func deploy<T: DeployableApp>(_ app: T) async throws {
let data = try await app.download()
try await app.install()
}
}

泛型和类型约束

泛型类型定义

  • 使用有意义的约束名称
  • 优先选择协议约束而不是类约束
  • 为复杂约束使用 where 子句
// ✅ Good
struct Repository<Entity: Identifiable & Codable> {
private var entities: [Entity.ID: Entity] = [:]

func save<T>(_ entity: T) where T == Entity {
entities[entity.id] = entity
}

func find<ID>(byId id: ID) -> Entity? where ID == Entity.ID {
return entities[id]
}
}

// ❌ Bad
struct Repository<T> {
private var items: [String: T] = [:]
// No type safety
}

高级泛型约束

  • 适当使用条件一致性
  • 在需要时利用幻影类型
// ✅ Good
extension Array: AppCollection where Element: App {
var installedApps: [Element] {
return filter { $0.isInstalled }
}

func sortedByInstallDate() -> [Element] {
return sorted { $0.installDate < $1.installDate }
}
}

// Phantom types for type safety
struct AppState<Status> {
let app: App
}

enum Downloaded {}
enum Installed {}

typealias DownloadedApp = AppState<Downloaded>
typealias InstalledApp = AppState<Installed>

异步/等待模式

异步函数设计

  • 一致地使用 async/await
  • 清晰地组织并发操作
  • 适当处理取消
// ✅ Good
actor AppInstallationManager {
private var activeInstallations: [String: Task<Void, Error>] = [:]

func installApp(_ app: App) async throws {
// Prevent duplicate installations
if activeInstallations[app.identifier] != nil {
throw InstallationError.alreadyInstalling
}

let task = Task {
try await performInstallation(app)
}

activeInstallations[app.identifier] = task

defer {
activeInstallations.removeValue(forKey: app.identifier)
}

try await task.value
}

private func performInstallation(_ app: App) async throws {
// Check for cancellation at key points
try Task.checkCancellation()

let data = try await downloadApp(app)

try Task.checkCancellation()

try await installData(data, for: app)
}
}

// ❌ Bad
func installApp(_ app: App, completion: @escaping (Error?) -> Void) {
DispatchQueue.global().async {
// Mixing old completion handler style with new async code
let result = await self.downloadApp(app)
DispatchQueue.main.async {
completion(nil)
}
}
}

结构化并发

  • 使用任务组进行相关的并发操作
  • 优先选择结构化并发而不是非结构化任务
// ✅ Good
func installMultipleApps(_ apps: [App]) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
for app in apps {
group.addTask {
try await self.installApp(app)
}
}

// Wait for all installations to complete
try await group.waitForAll()
}
}

// For independent results
func downloadMultipleApps(_ apps: [App]) async throws -> [App: Data] {
try await withThrowingTaskGroup(of: (App, Data).self) { group in
for app in apps {
group.addTask {
let data = try await self.downloadApp(app)
return (app, data)
}
}

var results: [App: Data] = [:]
for try await (app, data) in group {
results[app] = data
}
return results
}
}

Result Builders 和 DSL

自定义 Result Builders

  • 创建专注、单一用途的 Result Builders
  • 为领域特定操作提供清晰的语法
// ✅ Good
@resultBuilder
struct AppConfigurationBuilder {
static func buildBlock(_ components: AppConfigurationComponent...) -> AppConfiguration {
return AppConfiguration(components: components)
}

static func buildOptional(_ component: AppConfigurationComponent?) -> AppConfigurationComponent? {
return component
}

static func buildEither(first component: AppConfigurationComponent) -> AppConfigurationComponent {
return component
}

static func buildEither(second component: AppConfigurationComponent) -> AppConfigurationComponent {
return component
}
}

// Usage
func configureApp(@AppConfigurationBuilder builder: () -> AppConfiguration) -> App {
let config = builder()
return App(configuration: config)
}

let app = configureApp {
AppName("SideStore")
AppVersion("1.0.0")
if debugMode {
DebugSettings()
}
Permissions {
NetworkAccess()
FileSystemAccess()
}
}

高级 Objective-C 模式

分类组织

  • 使用分类(Categories)来组织相关功能
  • 保持分类名称具有描述性和特异性
// ✅ Good
@interface NSString (SSValidation)
- (BOOL)ss_isValidAppIdentifier;
- (BOOL)ss_isValidVersion;
@end

@interface UIViewController (SSAppInstallation)
- (void)ss_presentAppInstallationViewController:(SSApp *)app;
- (void)ss_showInstallationProgress:(SSInstallationProgress *)progress;
@end

// ❌ Bad
@interface NSString (Helpers)
- (BOOL)isValid; // Too generic
- (NSString *)cleanup; // Unclear purpose
@end

高级内存管理

  • 为委托关系使用正确的模式
  • 正确处理复杂的对象图
// ✅ Good
@interface SSAppInstaller : NSObject
@property (nonatomic, weak) id<SSAppInstallerDelegate> delegate;
@property (nonatomic, strong) NSOperationQueue *installationQueue;
@end

@implementation SSAppInstaller

- (instancetype)init {
self = [super init];
if (self) {
_installationQueue = [[NSOperationQueue alloc] init];
_installationQueue.maxConcurrentOperationCount = 3;
_installationQueue.name = @"com.sidestore.installation";
}
return self;
}

- (void)installApp:(SSApp *)app completion:(void (^)(NSError *))completion {
__weak typeof(self) weakSelf = self;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;

NSError *error = nil;
[strongSelf performInstallationForApp:app error:&error];

dispatch_async(dispatch_get_main_queue(), ^{
completion(error);
});
}];

[self.installationQueue addOperation:operation];
}

@end

Block 使用模式

  • 为异步操作使用正确的 Block 模式
  • 在 Block 中正确处理内存管理
// ✅ Good
typedef void (^SSInstallationProgressBlock)(float progress);
typedef void (^SSInstallationCompletionBlock)(SSApp *app, NSError *error);

@interface SSAppDownloader : NSObject
- (NSURLSessionTask *)downloadApp:(SSApp *)app
progress:(SSInstallationProgressBlock)progressBlock
completion:(SSInstallationCompletionBlock)completion;
@end

@implementation SSAppDownloader

- (NSURLSessionTask *)downloadApp:(SSApp *)app
progress:(SSInstallationProgressBlock)progressBlock
completion:(SSInstallationCompletionBlock)completion {

NSURLRequest *request = [NSURLRequest requestWithURL:app.downloadURL];

__weak typeof(self) weakSelf = self;
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession]
downloadTaskWithRequest:request
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;

if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
return;
}

// Process downloaded file
[strongSelf processDownloadedFile:location
forApp:app
completion:completion];
}];

[task resume];
return task;
}

@end

架构模式

MVVM 实现

  • 明确分离模型(Model)、视图(View)和视图模型(ViewModel)的职责
  • 使用正确的数据绑定模式
// ✅ Good
// Model
struct App {
let identifier: String
let name: String
let version: String
let isInstalled: Bool
}

// ViewModel
@MainActor
class AppListViewModel: ObservableObject {
@Published var apps: [App] = []
@Published var isLoading = false
@Published var errorMessage: String?

private let appService: AppService

init(appService: AppService) {
self.appService = appService
}

func loadApps() async {
isLoading = true
errorMessage = nil

do {
apps = try await appService.fetchAvailableApps()
} catch {
errorMessage = error.localizedDescription
}

isLoading = false
}

func installApp(_ app: App) async {
do {
try await appService.installApp(app)
await loadApps() // Refresh the list
} catch {
errorMessage = "Failed to install \(app.name): \(error.localizedDescription)"
}
}
}

// View
struct AppListView: View {
@StateObject private var viewModel: AppListViewModel

init(appService: AppService) {
_viewModel = StateObject(wrappedValue: AppListViewModel(appService: appService))
}

var body: some View {
NavigationView {
List(viewModel.apps, id: \.identifier) { app in
AppRowView(app: app) {
Task {
await viewModel.installApp(app)
}
}
}
.navigationTitle("Available Apps")
.overlay {
if viewModel.isLoading {
ProgressView()
}
}
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("OK") {
viewModel.errorMessage = nil
}
} message: {
Text(viewModel.errorMessage ?? "")
}
}
.task {
await viewModel.loadApps()
}
}
}

依赖注入

  • 使用依赖注入进行可测试性和灵活性
  • 对于复杂应用程序,考虑使用 DI 容器
// ✅ Good
protocol AppService {
func fetchAvailableApps() async throws -> [App]
func installApp(_ app: App) async throws
}

protocol NetworkService {
func downloadData(from url: URL) async throws -> Data
}

class DefaultAppService: AppService {
private let networkService: NetworkService
private let deviceService: DeviceService

init(networkService: NetworkService, deviceService: DeviceService) {
self.networkService = networkService
self.deviceService = deviceService
}

func fetchAvailableApps() async throws -> [App] {
let data = try await networkService.downloadData(from: appsURL)
return try JSONDecoder().decode([App].self, from: data)
}

func installApp(_ app: App) async throws {
guard deviceService.hasSpace(for: app) else {
throw InstallationError.insufficientStorage
}

let appData = try await networkService.downloadData(from: app.downloadURL)
try await deviceService.installApp(data: appData)
}
}

// DI Container
class ServiceContainer {
static let shared = ServiceContainer()

private init() {}

lazy var networkService: NetworkService = DefaultNetworkService()
lazy var deviceService: DeviceService = DefaultDeviceService()
lazy var appService: AppService = DefaultAppService(
networkService: networkService,
deviceService: deviceService
)
}

错误处理架构

  • 创建全面的错误处理策略
  • 使用类型化错误以更好地处理错误
// ✅ Good
enum SideStoreError: Error {
case network(NetworkError)
case installation(InstallationError)
case device(DeviceError)
case validation(ValidationError)
}

enum NetworkError: Error {
case noConnection
case timeout
case serverError(Int)
case invalidResponse
}

enum InstallationError: Error {
case insufficientStorage
case incompatibleDevice
case corruptedFile
case alreadyInstalled
}

extension SideStoreError: LocalizedError {
var errorDescription: String? {
switch self {
case .network(let networkError):
return "Network error: \(networkError.localizedDescription)"
case .installation(let installError):
return "Installation error: \(installError.localizedDescription)"
case .device(let deviceError):
return "Device error: \(deviceError.localizedDescription)"
case .validation(let validationError):
return "Validation error: \(validationError.localizedDescription)"
}
}
}

// Error handling in services
class AppService {
func installApp(_ app: App) async throws {
do {
try validateApp(app)
} catch {
throw SideStoreError.validation(error as! ValidationError)
}

do {
try await performInstallation(app)
} catch let error as NetworkError {
throw SideStoreError.network(error)
} catch let error as InstallationError {
throw SideStoreError.installation(error)
}
}
}

性能考量

内存优化

  • 对昂贵的资源使用延迟加载
  • 实施适当的缓存策略
// ✅ Good
class AppImageCache {
private let cache = NSCache<NSString, UIImage>()
private let downloadQueue = DispatchQueue(label: "image-download", qos: .utility)

init() {
cache.countLimit = 50
cache.totalCostLimit = 50 * 1024 * 1024 // 50MB
}

func image(for app: App) async -> UIImage? {
let key = app.identifier as NSString

// Check cache first
if let cachedImage = cache.object(forKey: key) {
return cachedImage
}

// Download if not cached
return await withCheckedContinuation { continuation in
downloadQueue.async { [weak self] in
guard let self = self else {
continuation.resume(returning: nil)
return
}

do {
let data = try Data(contentsOf: app.iconURL)
let image = UIImage(data: data)

if let image = image {
self.cache.setObject(image, forKey: key)
}

continuation.resume(returning: image)
} catch {
continuation.resume(returning: nil)
}
}
}
}
}

线程最佳实践

  • 使用适当的队列优先级
  • 最小化上下文切换
// ✅ Good
actor BackgroundProcessor {
private let processingQueue = DispatchQueue(
label: "background-processing",
qos: .utility,
attributes: .concurrent
)

func processLargeDataSet(_ data: [LargeDataItem]) async -> [ProcessedItem] {
return await withTaskGroup(of: ProcessedItem?.self, returning: [ProcessedItem].self) { group in
let chunkSize = max(data.count / ProcessInfo.processInfo.activeProcessorCount, 1)

for chunk in data.chunked(into: chunkSize) {
group.addTask {
return await self.processChunk(chunk)
}
}

var results: [ProcessedItem] = []
for await result in group {
if let processed = result {
results.append(processed)
}
}

return results
}
}

private func processChunk(_ chunk: [LargeDataItem]) async -> ProcessedItem? {
// CPU-intensive processing
return await withCheckedContinuation { continuation in
processingQueue.async {
let result = chunk.map { self.expensiveOperation($0) }
continuation.resume(returning: ProcessedItem(results: result))
}
}
}
}

测试模式

基于协议的测试

  • 在测试中使用协议进行依赖注入
  • 创建专注于功能测试的替身(Test Doubles)
// ✅ Good
class MockAppService: AppService {
var shouldFailInstallation = false
var installedApps: [App] = []

func fetchAvailableApps() async throws -> [App] {
return [
App(identifier: "test.app1", name: "Test App 1", version: "1.0.0"),
App(identifier: "test.app2", name: "Test App 2", version: "2.0.0")
]
}

func installApp(_ app: App) async throws {
if shouldFailInstallation {
throw InstallationError.insufficientStorage
}
installedApps.append(app)
}
}

class AppListViewModelTests: XCTestCase {
private var mockAppService: MockAppService!
private var viewModel: AppListViewModel!

override func setUp() {
super.setUp()
mockAppService = MockAppService()
viewModel = AppListViewModel(appService: mockAppService)
}

@MainActor
func testLoadAppsSuccess() async {
await viewModel.loadApps()

XCTAssertEqual(viewModel.apps.count, 2)
XCTAssertFalse(viewModel.isLoading)
XCTAssertNil(viewModel.errorMessage)
}

@MainActor
func testInstallAppFailure() async {
mockAppService.shouldFailInstallation = true

let testApp = App(identifier: "test", name: "Test", version: "1.0")
await viewModel.installApp(testApp)

XCTAssertNotNil(viewModel.errorMessage)
XCTAssertTrue(viewModel.errorMessage!.contains("insufficient storage"))
}
}

文档标准

复杂 API 文档

  • 文档复杂行为和边缘情况
  • 为非平凡的 API 提供使用示例
/**
* 这是一个线程安全的管理器,用于处理具有自动重试逻辑的应用程序安装。
* 此类管理 iOS 应用程序的安装过程,处理网络下载、签名验证和设备通信。
* 它为瞬时故障提供自动重试功能,并提供全面的错误报告。
*
* ## Usage Example
* ```swift
* let installer = AppInstallationManager()
*
* do {
* let result = try await installer.installApp(
* from: app.sourceURL,
* identifier: app.bundleID,
* maxRetries: 3
* )
* print("Installation completed: \(result.installedPath)")
* } catch InstallationError.insufficientStorage {
* // Handle storage error
* } catch {
* // Handle other errors
* }
* ```
*
* ## 线程安全
* 此类是线程安全的,可以从任何队列调用。除非另有说明,所有完成处理程序都在主队列上调用。
* ## 错误处理
* 安装失败分为可恢复错误和不可恢复错误。可恢复错误(网络超时、临时设备问题)将自动重试至指定限制。
* 不可恢复错误(无效签名、不兼容设备)将立即失败。
*/
@MainActor
class AppInstallationManager {
// Implementation
}

请记住:这些高级模式应谨慎使用。始终优先考虑代码清晰度和可维护性,而不是巧妙的实现。如有疑问,请选择能够满足需求的更简单方法。