iOS中如何在for循环内等待进程完成后再进入下一次迭代?
Got it, let's break down how to fix this. Your current code fires off all 5 NSURLSessionDataTasks at once because they run asynchronously—so the for loop doesn't pause for each task to finish before moving to the next iteration. Here are a few reliable approaches, tailored to different iOS version requirements:
1. Using Dispatch Semaphores (Legacy Compatible)
This works for all iOS versions and uses GCD to block the loop until each async task completes. Critical note: Never run this on the main thread (it'll freeze your app's UI). Always use a background queue for this kind of synchronous waiting.
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.yourdomain.requestQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(backgroundQueue, ^{ for (int i = 0; i < 5; i++) { NSLog(@"%d Iteration started", i); // Create a semaphore to wait for the task to finish dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"%d iteration completed ", i); // Signal the semaphore that we're done dispatch_semaphore_signal(semaphore); }] resume]; // Wait until the semaphore is signaled dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_release(semaphore); // Skip this line if using ARC } });
2. Using OperationQueue (Controlled Concurrency)
If you prefer an object-oriented approach, OperationQueue lets you queue tasks and enforce sequential execution by setting maxConcurrentOperationCount to 1. Each network task is wrapped in an NSBlockOperation:
NSOperationQueue *serialQueue = [[NSOperationQueue alloc] init]; serialQueue.maxConcurrentOperationCount = 1; // Only one task runs at a time for (int i = 0; i < 5; i++) { NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%d Iteration started", i); // Use a semaphore inside the operation to wait for the async task dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"%d iteration completed ", i); dispatch_semaphore_signal(semaphore); }] resume]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_release(semaphore); // Skip this line if using ARC }]; [serialQueue addOperation:operation]; }
3. Async/Await (Modern iOS 15+)
If you're targeting iOS 15 or later, Objective-C now supports async/await, which makes this code much cleaner and avoids manual semaphores. First, wrap the dataTaskWithRequest: call into an async helper method:
// Declare an async helper method - (NSData * _Nullable)fetchDataWithRequest:(NSURLRequest *)request error:(NSError **)error async { __block NSData *resultData; __block NSError *resultError; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable taskError) { resultData = data; resultError = taskError; dispatch_semaphore_signal(semaphore); }] resume]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if (error) *error = resultError; return resultData; } // Then use it in your loop (must be called from an async context) - (void)runSequentialTasks async { for (int i = 0; i < 5; i++) { NSLog(@"%d Iteration started", i); NSError *error; NSData *data = [self fetchDataWithRequest:request error:&error]; NSLog(@"%d iteration completed ", i); if (error) { // Handle error if needed NSLog(@"Error in iteration %d: %@", i, error.localizedDescription); } } }
Bonus: Swift Version (iOS 13+)
If you're using Swift, async/await is even more straightforward (available in iOS 13+ with Combine, or iOS 15+ with native async/await):
// iOS 15+ native async/await func runSequentialTasks() async { for i in 0..<5 { print("\(i) Iteration started") do { let (data, response) = try await URLSession.shared.data(for: request) print("\(i) iteration completed") } catch { print("Error in iteration \(i): \(error.localizedDescription)") } } } // Call it from an async context, e.g.: // Task { // await runSequentialTasks() // }
Key Tips:
- Avoid blocking the main thread: All these approaches should run on a background queue/async context to keep your app responsive.
- Add timeouts: If you don't want to wait indefinitely, replace
DISPATCH_TIME_FOREVERwith a specific timeout (e.g.,dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)for 10 seconds). - Don't skip error handling: Always add checks for network errors to avoid silent failures in your iterations.
内容的提问来源于stack exchange,提问作者Anil Kumar R

