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

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,在回调中处理加载完成后的逻辑即可。


关键说明

  1. 为什么不用全局变量:全局变量容易导致多个地方修改数据,引发状态不一致,调试和维护都很麻烦。用类的实例属性封装数据逻辑,更符合iOS的MVC/MVVM架构。
  2. 异步回调的必要性:网络请求是异步操作,必须用回调(或者Combine、Async/Await)来处理结果,不能直接在函数外同步访问数据。
  3. JSON结构适配:上面的代码假设你的API返回的是{"results": [...], ...}结构,如果你的JSON直接是题目数组,记得调整解析逻辑(比如直接解析[QuizQuestion].self)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:56:05