Swift 4 测验应用JSON解析全局变量问题求助
解决Swift 4测验应用的JSON解析与变量作用域问题
嘿,我明白你遇到的问题了——把解析后的数组放在函数里当局部变量,想改成全局又搞不定,确实挺头疼的。其实不建议用全局变量(容易导致状态混乱、难以维护),更好的方式是用类的实例属性来管理数据,配合回调或者异步处理来获取解析结果。下面我给你两种可行的方案,附完整可运行代码:
方案一:继续使用SwiftyJSON + Alamofire
这个方案基于你当前用的工具,重点解决变量作用域问题,同时让代码更符合iOS开发的架构习惯。
步骤1:导入依赖
确保你的项目已经集成了Alamofire和SwiftyJSON(比如用CocoaPods或者Swift Package Manager),然后在代码里导入:
import Alamofire import SwiftyJSON
步骤2:定义模型结构体
根据你的JSON结构,创建一个模型来存储题目数据:
struct QuizQuestion { let question: String? let correctAnswer: String? let incorrectAnswers: [String]? // 假设你需要存储错误选项 // 从SwiftyJSON对象初始化模型 init(from json: JSON) { question = json["question"].string correctAnswer = json["correct_answer"].string incorrectAnswers = json["incorrect_answers"].array?.compactMap { $0.string } } }
步骤3:创建数据管理类
用一个类来封装数据请求和存储逻辑,替代全局变量:
class QuizManager { // 用实例属性存储题目数组,替代全局变量 var questions: [QuizQuestion] = [] // 定义回调类型,用于通知数据加载完成 typealias FetchCompletion = (Error?) -> Void func fetchQuizQuestions(completion: @escaping FetchCompletion) { let apiURL = "你的测验API地址" // 替换成实际的API链接 Alamofire.request(apiURL).responseJSON { [weak self] response in guard let self = self else { return } switch response.result { case .success(let value): let json = JSON(value) // 这里假设你的API返回的是{"results": [...]}结构,根据实际JSON调整 if let questionsJSONArray = json["results"].array { self.questions = questionsJSONArray.map { QuizQuestion(from: $0) } completion(nil) // 加载成功,回调无错误 } else { completion(NSError(domain: "QuizError", code: 0, userInfo: [NSLocalizedDescriptionKey: "JSON结构不匹配"])) } case .failure(let error): completion(error) // 加载失败,返回错误信息 } } } }
步骤4:在ViewController中使用
class QuizViewController: UIViewController { let quizManager = QuizManager() override func viewDidLoad() { super.viewDidLoad() // 请求题目数据 quizManager.fetchQuizQuestions { [weak self] error in guard let self = self else { return } if let error = error { print("加载题目失败:\(error.localizedDescription)") return } // 数据加载完成,现在可以访问quizManager.questions了 print("共加载到 \(self.quizManager.questions.count) 道题目") if let firstQuestion = self.quizManager.questions.first { print("第一题:\(firstQuestion.question ?? "无题目内容")") } // 这里可以更新UI,比如把题目显示到界面上 } } }
方案二:使用Swift原生Codable(无需第三方库)
Swift 4开始支持的Codable协议可以直接解析JSON,不需要SwiftyJSON,更轻量且原生支持,推荐尝试:
步骤1:定义Codable模型
// 模型遵循Codable,自动处理JSON解析 struct QuizQuestion: Codable { let question: String? let correctAnswer: String? let incorrectAnswers: [String]? // 映射JSON的蛇形命名(比如correct_answer)到Swift的驼峰命名 enum CodingKeys: String, CodingKey { case question case correctAnswer = "correct_answer" case incorrectAnswers = "incorrect_answers" } } // 如果你的API返回的是包含results数组的外层对象,比如{"results": [...], "response_code": 0} struct QuizResponse: Codable { let results: [QuizQuestion] let responseCode: Int? enum CodingKeys: String, CodingKey { case results case responseCode = "response_code" } }
步骤2:数据管理类(用Alamofire或原生URLSession)
class QuizManager { var questions: [QuizQuestion] = [] typealias FetchCompletion = (Error?) -> Void // 用Alamofire的Codable扩展 func fetchQuizQuestions(completion: @escaping FetchCompletion) { let apiURL = "你的测验API地址" Alamofire.request(apiURL).responseDecodable(of: QuizResponse.self) { [weak self] response in guard let self = self else { return } switch response.result { case .success(let quizResponse): self.questions = quizResponse.results completion(nil) case .failure(let error): completion(error) } } // 如果你不想用Alamofire,也可以用原生URLSession: /* guard let url = URL(string: apiURL) else { completion(NSError(domain: "QuizError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无效的URL"])) return } URLSession.shared.dataTask(with: url) { [weak self] data, _, error in guard let self = self else { return } if let error = error { completion(error) return } guard let data = data else { completion(NSError(domain: "QuizError", code: 2, userInfo: [NSLocalizedDescriptionKey: "未获取到数据"])) return } do { let quizResponse = try JSONDecoder().decode(QuizResponse.self, from: data) self.questions = quizResponse.results completion(nil) } catch { completion(error) } }.resume() */ } }
步骤3:ViewController使用方式和方案一完全一致
调用quizManager.fetchQuizQuestions,在回调中处理加载完成后的逻辑即可。
关键说明
- 为什么不用全局变量:全局变量容易导致多个地方修改数据,引发状态不一致,调试和维护都很麻烦。用类的实例属性封装数据逻辑,更符合iOS的MVC/MVVM架构。
- 异步回调的必要性:网络请求是异步操作,必须用回调(或者Combine、Async/Await)来处理结果,不能直接在函数外同步访问数据。
- JSON结构适配:上面的代码假设你的API返回的是
{"results": [...], ...}结构,如果你的JSON直接是题目数组,记得调整解析逻辑(比如直接解析[QuizQuestion].self)。
内容的提问来源于stack exchange,提问作者Anshul Shah
相关产品推荐
相关产品推荐

