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() } } } }
额外优化建议
- 把验证逻辑封装成单独的方法,比如
validateUserData() -> Bool,这样代码更清晰。 - 使用
hasError直接标记是否有错误,不需要accessArray也能判断,accessArray可以作为日志用,或者直接去掉,简化逻辑。 - 使用
[weak self]避免循环引用,防止内存泄漏。 - 错误提示不要重复弹出,比如如果已经因为没填字段弹出提示,就不要再因为用户名空重复弹出。
内容的提问来源于stack exchange,提问作者user12173345

