You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为URLSession实现依赖注入时触发BAD ACCESS无限循环错误求助

Fixing BAD ACCESS Crash with URLSession.shared & Dependency Injection for Unit Testing

Hey there! Let's tackle that frustrating BAD ACCESS crash you're hitting when using URLSession.shared while setting up dependency injection for mock-based unit testing.

First, Why Is This Happening?

That infinite repeat of URLSession.dataTask(with:completionHandler) until crash almost always boils down to one of two issues:

  • Unlimited recursive requests: If your completion handler logic re-triggers the same network call (like an unguarded retry on failure) without a stop condition, you’ll spawn an endless chain of tasks that gobble up memory until the app crashes.
  • Retain cycles: If you’re capturing self in your completion handler without using weak/unowned, you might create a cycle that prevents memory from being released—combining this with recursive calls makes the crash happen even faster.

The Fix: Proper Dependency Injection + Clean Request Logic

Let’s walk through the right way to structure your code to avoid this crash and enable mock testing.

1. Abstract URLSession with a Protocol

First, create a protocol to decouple your network code from the concrete URLSession class. This makes mocking trivial:

protocol NetworkSession {
    func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
}

// Make the real URLSession conform to our protocol
extension URLSession: NetworkSession {}

2. Inject the Session into Your Network Service

Instead of hardcoding URLSession.shared inside your service, inject it via the initializer. This lets you use the real session in production and a mock in tests:

class NetworkService {
    private let session: NetworkSession
    
    // Default to URLSession.shared for real-world use
    init(session: NetworkSession = URLSession.shared) {
        self.session = session
    }
    
    func fetchData(from url: URL, completion: @escaping (Result<Data, Error>) -> Void) {
        let task = session.dataTask(with: url) { [weak self] data, response, error in
            // Guard against self being deallocated mid-execution
            guard let self = self else { return }
            
            // Handle errors without infinite retries!
            if let error = error {
                completion(.failure(error))
                return // Critical: don't re-call fetchData here without a retry limit
            }
            
            guard let responseData = data else {
                completion(.failure(NetworkError.noData))
                return
            }
            
            completion(.success(responseData))
        }
        task.resume()
    }
}

// Helper error enum for clarity
enum NetworkError: Error {
    case noData
}

3. Fix the Root Crash Cause

  • Add retry limits: If you do need retry logic, implement a counter that stops after a set number of attempts (e.g., 3 retries max) instead of looping infinitely.
  • Use weak self: Always capture self weakly in completion handlers to avoid retain cycles—this ensures objects can be deallocated properly when they’re no longer needed.

Mocking for Unit Testing

With the protocol in place, creating a mock session is straightforward. For example:

class MockNetworkSession: NetworkSession {
    var mockData: Data?
    var mockError: Error?
    
    func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
        // Immediately trigger the completion with our mock data/error
        completionHandler(mockData, nil, mockError)
        
        // Return a dummy task (we won't resume it in tests)
        return URLSessionDataTask()
    }
}

You can then use this mock in your tests to simulate success/failure cases without hitting real network calls.


内容的提问来源于stack exchange,提问作者SwiftyJD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:38:34