Ios 如何在swift中的某些视图控制器之间正确共享变量?

Ios 如何在swift中的某些视图控制器之间正确共享变量?,ios,swift,Ios,Swift,我是编程和iOS开发的初学者,我正在尝试制作一个使用Tab Bar控制器的应用程序,它有5个Tab Bar 我创建了一个User类,该类将电子邮件、uid、姓名和其他用户数据(如他们的住所等)作为属性。该类在选项卡栏控制器(索引:0)的第一个选项卡栏的视图控制器中初始化,我们称之为HomeVC。使用来自服务器的数据初始化User类的实例 此用户类的实例将在我的应用程序中多次使用。比方说,当用户在位于第三个选项卡栏(索引:2)导航堆栈底部/最后一个VC的PostVC中创建帖子时。当用户在profi

我是编程和iOS开发的初学者,我正在尝试制作一个使用Tab Bar控制器的应用程序,它有5个Tab Bar

我创建了一个
User
类,该类将电子邮件、uid、姓名和其他用户数据(如他们的住所等)作为属性。该类在选项卡栏控制器(索引:0)的第一个选项卡栏的视图控制器中初始化,我们称之为HomeVC。使用来自服务器的数据初始化
User
类的实例

用户
类的实例将在我的应用程序中多次使用。比方说,当用户在位于第三个选项卡栏(索引:2)导航堆栈底部/最后一个VC的PostVC中创建帖子时。当用户在profileVC中看到自己的配置文件时也是如此

我认为反复请求在我的应用程序中的某个位置获取用户数据不是一个好的做法,因为目前我使用的是Firestore数据库,更多地读取数据库意味着需要更多的成本。所以我只想向服务器发出一个请求,获取用户数据,然后将该用户数据用于我的整个应用程序

此时,我创建了一个超级全局变量(一个位于类之外的变量),这样我就可以在我的应用程序中的任何地方访问它。但我刚刚读到,使用超全局变量是一种糟糕的编程实践


那我该怎么办?是否必须使用
prepareforsgue
传递用户数据?有没有更好的办法来解决这个问题?

尽管如此,人们还是讨厌单身汉

URLSession.shared

UIApplication.shared

NotificationCenter.default

此外,几乎每个Apple框架都有一个singleton类,这不是一个坏主意,只要它有很好的文档记录,这样其他开发人员就可以正确地阅读您的代码

,因为您已经在使用
Cloud Firestore
,您可以使用
Firebase
身份验证仪表板中的各种方法为您的用户签名(
FirebaseAuth
)。用户登录后,您可以从应用程序中的任何位置获取当前用户,如下所示:

    import Firebase        

    if let user = Auth.auth().currentUser {
        let userID = user.uid
        let email = user.email
        let displayName = user.displayName
    }
struct User {
    let userid: String
    let email: String
}
应用程序拥有有效的用户令牌后,不会继续回拨服务器,除非令牌需要刷新(例如在更改用户帐户设置期间)


我认为你应该避免单身。我学到的最好的解决方案之一是为你的数据管理器创建协议(如会话、购物车…)。之后,您应该尽可能地将依赖项推离类。在本例中,它是AppDelegate。因此,您可以为这些类创建属性以保存在那里。如果您有许多这些类,请将它们放在一个类或其他内容中

小例子:

@objc protocol SessionManagerProtocol {
  func persistUser()
}
然后在AppDelegate中:(确保线程安全)

使用此解决方案,如果希望使其可测试,还可以注入依赖项

如果应用程序进入后台模式,则将会话保存到UserDefault或您自己的数据库


如果您只想在ViewController之间传递数据,请查看以下链接:

我认为对于您的情况,您可以将数据以序列化格式存储在某个file.txt或CoreData DB中。您可以随时从应用程序的任何部分获取这些位置的数据。

我建议您在此处也避免使用单例。主要动机是让事情能够测试这是一个可能的方法

您有一个
用户
类似这样的用户:

    import Firebase        

    if let user = Auth.auth().currentUser {
        let userID = user.uid
        let email = user.email
        let displayName = user.displayName
    }
struct User {
    let userid: String
    let email: String
}
声明用于管理用户的协议。使用协议使测试更容易,因为您可以创建符合该协议的模拟

protocol UserManagerProtocol {
    func getSignedInUser() -> User?
    func signIn(userid: String, password: String) -> User?
}
声明实现协议的实际类:

class UserManager: UserManagerProtocol {
    fileprivate var signedInUser: User?
    func getSignedInUser() -> User? {
        return signedInUser
    }
    func signIn(userid: String, password: String) -> User? {
        //call your server to sign in user
        // if sign in fails, return nil
        // if sign in works, create a User object
        signedInUser = User(userid: userid, email: "email retrieved from server")
        return signedInUser
    }
}
在视图控制器中添加引用,但使用协议声明它,而不是实际的类。同样,这允许模拟

class HomeVC: UIViewController {
    var userManager: UserManagerProtocol?
}
class PostVC: UIViewController {
    var userManager: UserManagerProtocol?
}
最后,在AppDelegate中,当您启动应用程序时,创建一个
UserManager
实例,将其存储在AppDelegate中,并将其传递给所有视图控制器。在本例中,我显然遗漏了很多内容,因为我不知道AppDelegate的外观

class AppDelegate: NSObject, UIApplicationDelegate {
    var userManager: UserManagerProtocol?

    func applicationDidFinishLaunching(_ application: UIApplication) {
        userManager = UserManager()

        //when setting up your tab bar controllers, pass the userManager instance to each of them
    }
}

您应该将您的用户对象存储在UserDefaults中您是否使用
FirebaseAuth
登录用户?创建一个DBManager类并使用类似数据库的领域。实用程序类的单例与数据模型的单例之间存在巨大差异。人们并不讨厌单例。许多人不喜欢在re不应该使用它们。通知的对象和用户信息属性不是吗?数据模型的示例是吗???
object
userInfo
不是单例(也不是属性)。它们是方法调用的简单参数。此处的注释不是编写有关在应用程序中传递数据的正确方法的冗长讨论的地方。它们是notificationCenter发送的通知中的属性,这意味着singleton共享数据,加上例如firebase、parse和许多其他人与sin共享数据gletons,我告诉过你,由于没有解决办法,它将不得不写和读数据,但性能不充分,这肯定与app live相比,存在一个单例puls的能力将其交给下一个类,这是可怕的