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

Swift中accessGiven变量变更为何未被全局识别?注册页代码疑问

问题根源分析与解决方案

你的代码里的核心问题是异步操作的时序问题,以及状态管理逻辑的混乱,导致accessArray和accessGiven的变更看起来被忽略了,具体原因如下:

1. 异步回调执行时机晚于同步判断

你代码中的Auth.auth().fetchProviders(forEmail:)和ref.child("users").observeSingleEvent(of:)都是异步网络请求——这些方法会立即返回,而它们的回调闭包会在网络请求完成后才执行(通常是在当前nextPressed方法执行完之后)。

但你在这些异步方法之后直接写了:

if accessArray.contains(2) {
    print("Access not Given.")
} else {
    // 执行注册逻辑
}

这行代码是同步执行的,此时异步回调里的accessArray.append(+2)或append(+1)还没执行,所以accessArray里根本没有异步操作添加的状态标记,自然会错误地认为所有验证都通过了。

2. 同步验证逻辑的状态覆盖问题

你的同步验证(比如图片、用户名、密码等判断)是依次执行的,每一个判断都会覆盖accessGiven的值,并且不管前面有没有错误,都会继续执行后面的判断,导致accessArray里会有大量冗余的标记,比如如果用户没选头像,又没填用户名,accessArray会先加2,再加1,再加2,最后状态混乱,无法准确反映验证结果。


修复方案

步骤1:重构验证逻辑,先完成所有同步验证,再处理异步验证

先把所有可以同步完成的验证(图片、用户名、密码格式、密码匹配、邮箱格式等)先做完,收集错误状态;然后再执行异步验证(邮箱是否已注册、用户名是否已存在),等所有异步验证完成后,再统一判断是否可以执行注册。

步骤2:用组队列管理异步操作

使用DispatchGroup来等待所有异步操作完成,这样就能确保在判断accessArray之前,所有验证的状态都已经被正确添加进去。

修复后的代码示例

@IBAction func nextPressed(_ sender: Any) {
    var accessArray = [Int]()
    var hasError = false

    // 1. 同步验证:头像检查
    if imageView.image == nil {
        alerts(title: "Ooops...", message: "Please select a profile picture.")
        accessArray.append(2)
        hasError = true
    } else {
        accessArray.append(1)
    }

    // 2. 同步验证:字段非空检查(合并判断,避免重复提示)
    if usernameField.text?.isEmpty ?? true || emailField.text?.isEmpty ?? true || password.text?.isEmpty ?? true || comPwField.text?.isEmpty ?? true {
        if !hasError { // 只在还没提示错误的时候弹出
            alerts(title: "Oops...", message: "Please fill out the fields.")
        }
        accessArray.append(2)
        hasError = true
    } else {
        accessArray.append(1)
    }

    // 3. 同步验证:用户名非空(其实上面已经包含,这里可以合并)
    if usernameField.text?.isEmpty ?? true {
        if !hasError {
            alerts(title: "Oops...", message: "Username can't be blank.")
        }
        accessArray.append(2)
        hasError = true
    } else {
        accessArray.append(1)
    }

    // 4. 同步验证:密码匹配
    if password.text != comPwField.text {
        if !hasError {
            alerts(title: "Oops...", message: "Passwords don't match.")
        }
        accessArray.append(2)
        hasError = true
    } else {
        accessArray.append(1)
    }

    // 5. 同步验证:邮箱格式
    let isValidEmail = isValidEmail(testStr: emailField.text ?? "")
    if !isValidEmail {
        if !hasError {
            alerts(title: "Oops...", message: "Invalid E-Mail.")
        }
        accessArray.append(2)
        hasError = true
    } else {
        accessArray.append(1)
    }

    // 6. 同步验证:密码长度
    let passwordLength = password.text?.count ?? 0
    let comPwLength = comPwField.text?.count ?? 0
    if passwordLength < 6 || comPwLength < 6 {
        if !hasError {
            alerts(title: "Oops...", message: "Password isn't longer than 6 characters.")
        }
        accessArray.append(2)
        hasError = true
    } else {
        accessArray.append(1)
    }

    // 如果同步验证已经有错误,直接返回,不用执行异步请求
    if hasError {
        print("Access not Given (sync validation failed).")
        print(accessArray)
        return
    }

    // 7. 异步验证:使用DispatchGroup等待所有异步操作完成
    let dispatchGroup = DispatchGroup()

    // 异步验证:邮箱是否已注册
    dispatchGroup.enter()
    Auth.auth().fetchProviders(forEmail: emailField.text!) { [weak self] stringArray, error in
        defer { dispatchGroup.leave() }
        guard let self = self else { return }
        
        if let error = error {
            print(error)
            self.accessArray.append(2)
            hasError = true
        } else {
            if let _ = stringArray {
                print("There is an active account using this E-mail")
                self.alerts(title: "Oops...", message: "There is an active account using this E-Mail.")
                self.accessArray.append(2)
                hasError = true
            } else {
                print("No active account using this email.")
                self.accessArray.append(1)
            }
        }
    }

    // 异步验证:用户名是否已存在
    dispatchGroup.enter()
    ref.child("users")
        .queryOrdered(byChild:"username")
        .queryEqual(toValue: usernameField.text!)
        .observeSingleEvent(of: .value) { [weak self] snapshot in
            defer { dispatchGroup.leave() }
            guard let self = self else { return }
            
            if snapshot.exists() {
                print("Username already in use.")
                self.alerts(title: "Oops", message: "Username already taken.")
                self.accessArray.append(2)
                hasError = true
            } else {
                print("Username not in use.")
                self.accessArray.append(1)
            }
        }

    // 等待所有异步验证完成
    dispatchGroup.notify(queue: .main) { [weak self] in
        guard let self = self else { return }
        
        if hasError || self.accessArray.contains(2) {
            print("Access not Given.")
            print(self.accessArray)
        } else {
            print("Access Given")
            // 执行注册逻辑
            Auth.auth().createUser(withEmail: self.emailField.text!, password: self.password.text!) { user, error in
                if let error = error {
                    print(error.localizedDescription)
                    return
                }
                guard let user = user else { return }
                
                // 更新用户信息
                let changeRequest = Auth.auth().currentUser!.createProfileChangeRequest()
                changeRequest.displayName = self.usernameField.text!
                changeRequest.commitChanges(completion: nil)
                
                // 上传头像
                let imageRef = self.userStorage.child("\(user.uid).jpg")
                guard let imageData = UIImageJPEGRepresentation(self.imageView.image!, 0.5) else { return }
                let uploadTask = imageRef.putData(imageData, metadata: nil) { metadata, err in
                    if let err = err {
                        print(err.localizedDescription)
                        return
                    }
                    imageRef.downloadURL { url, er in
                        if let er = er {
                            print(er.localizedDescription)
                            return
                        }
                        guard let url = url else { return }
                        
                        let userInfo: [String : Any] = [
                            "uid" : user.uid,
                            "username" : self.usernameField.text!,
                            "email" : self.emailField.text!,
                            "urlToImage" : url.absoluteString
                        ]
                        self.ref.child("users").child(user.uid).setValue(userInfo)
                        // 这里可以执行跳转逻辑
                        // self.performSegue(withIdentifier: "segue.SignUp.toTabBar", sender: nil)
                    }
                }
                uploadTask.resume()
            }
        }
    }
}

额外优化建议

  1. 把验证逻辑封装成单独的方法,比如validateUserData() -> Bool,这样代码更清晰。
  2. 使用hasError直接标记是否有错误,不需要accessArray也能判断,accessArray可以作为日志用,或者直接去掉,简化逻辑。
  3. 使用[weak self]避免循环引用,防止内存泄漏。
  4. 错误提示不要重复弹出,比如如果已经因为没填字段弹出提示,就不要再因为用户名空重复弹出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:45:14