如何将闭包中的变量移至其他函数?iOS用户数据迁移需求
Got it, let's break down how to solve this. Your goal is to replace the hardcoded user array in generateUsersData() with data fetched asynchronously from usernameList() and idList()—here's a step-by-step solution that works with async network calls:
Step 1: Update UsersData.generateUsersData() for async data fetching
Since usernameList() and idList() are async (they use completion closures), we can't just return an array directly. Instead, we'll modify generateUsersData() to take its own completion handler, use a DispatchGroup to wait for both API calls to finish, then assemble the User array.
import Foundation // Assume your User model looks like this (adjust if needed) struct User { let id: String let username: String } final class UsersData { static func generateUsersData(completion: @escaping ([User]?, Error?) -> Void) { let dispatchGroup = DispatchGroup() var fetchedUsernames: [String]? var fetchedIds: [String]? var requestError: Error? // Fetch usernames dispatchGroup.enter() usernameList { inputArray, error in defer { dispatchGroup.leave() } if let error = error { requestError = error return } fetchedUsernames = inputArray } // Fetch IDs dispatchGroup.enter() idList { idArray, error in defer { dispatchGroup.leave() } if let error = error { requestError = error return } fetchedIds = idArray } // Wait for both requests to complete, then process data dispatchGroup.notify(queue: .main) { // Handle any request errors first if let error = requestError { completion(nil, error) return } // Make sure we have matching counts of usernames and IDs guard let usernames = fetchedUsernames, let ids = fetchedIds, usernames.count == ids.count else { let mismatchError = NSError( domain: "UsersDataError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Usernames and IDs count don't match"] ) completion(nil, mismatchError) return } // Zip IDs and usernames into User objects let users = zip(ids, usernames).map { User(id: $0.0, username: $0.1) } completion(users, nil) } } // Stub for your existing usernameList function (adjust signature if needed) private static func usernameList(completion: ([String], Error?) -> Void) { // Your actual network request logic here completion(["Bill@comcast.net", "Oscar Peterson@yahoo.com", "DaveBrubeck@gmail.com"], nil) } // Stub for your existing idList function (adjust signature if needed) private static func idList(completion: ([String], Error?) -> Void) { // Your actual network request logic here completion(["111", "222", "333"], nil) } }
Step 2: Integrate with your TableViewController
Now, in your TableViewController, you'll call the updated generateUsersData() method, update your data source, and reload the table once the data is ready:
import UIKit class TableViewController: UITableViewController { private var users: [User] = [] private let cellIdentifier = "UserCell" override func viewDidLoad() { super.viewDidLoad() tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier) fetchUsers() } private func fetchUsers() { UsersData.generateUsersData { [weak self] users, error in guard let self = self else { return } if let error = error { // Handle error (e.g., show an alert to the user) print("Failed to load users: \(error.localizedDescription)") return } if let fetchedUsers = users { self.users = fetchedUsers self.tableView.reloadData() } } } // MARK: - Table view data source override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { users.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) let user = users[indexPath.row] cell.textLabel?.text = user.username cell.detailTextLabel?.text = user.id return cell } }
Key Things to Remember
- Async Completion Handlers: We use a completion handler because network requests don't block the main thread—we have to wait for them to finish before processing the data.
- DispatchGroup: This ensures we don't try to create
Userobjects until both the username and ID lists are fully fetched. - Error Handling: We catch network errors and also handle cases where the number of usernames doesn't match the number of IDs (to avoid crashes).
- Weak Self: Using
[weak self]in the completion handler prevents memory leaks by breaking the retain cycle between the view controller and the async closure.
内容的提问来源于stack exchange,提问作者X 1 go

