尝试使用依赖倒置存储值时崩溃
crash while try store value using dependency inversion
我想在我的应用程序中的应用程序委托中实现依赖倒置,因为我的 rootController
是我的 UITabBarController
但是当我想尝试它时出现错误
Fatal error: Unexpectedly found nil while unwrapping optional value
这是我在 appDelagate 中的代码
let exploreStore = ExploreStore()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let rootController = (window?.rootViewController as? UITabBarController)?.children.first as? ExploreViewController
// Inject Data
rootController?.exploreStore = exploreStore
return true
}
这是我的探索class
class ExploreStore {
fileprivate var allItems = [ExploreItem]()
func fetch() {
for data in loadData() {
allItems.append(ExploreItem(dict: data))
}
}
func numberOfItem() -> Int {
return allItems.count
}
func explore(at index: IndexPath) -> ExploreItem {
return allItems[index.item]
}
fileprivate func loadData() -> [[String: AnyObject]] {
guard
let path = Bundle.main.path(forResource: "ExploreData", ofType: "plist"),
let items = NSArray(contentsOfFile: path)
else { return [[:]] }
return items as! [[String: AnyObject]]
}
}
这是我的 exlporeViewController
var exploreStore: ExploreStore!
override func viewDidLoad() {
super.viewDidLoad()
// This is where the error found nil
exploreStore.fetch()
}
实际上,如果我不使用依赖倒置,代码就可以工作,比如我的探索视图控制器不使用像这样的强制解包
var exploreStore = ExploreStore()
但是由于我想获得知识并学习使用依赖倒置的 S.O.L.I.D 原则,所以我想坚持这个原则。
如果我正确理解了你的问题,你想在 AppDelegate
class 处初始化你的 class,然后你想将它传递给 UITabBarController
的第一个 children
为此,您需要对 didFinishLaunchingWithOptions
方法进行一些修改,如下所示:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "tabBar")
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = vc
let myTabBar = self.window?.rootViewController as! UITabBarController
let firstViewController = myTabBar.children.first as? FirstViewController
firstViewController?.exploreStore = exploreStore
self.window?.makeKeyAndVisible()
return true
}
我在这里做了一些修改,因为我正在从 Bundle
中检索 Info.plist
,而您的 ExploreStore
看起来像:
class ExploreStore {
var allItems = [ExploreItem]()
func fetch() {
if let dataObjectFromPlist = loadData() {
allItems.append(ExploreItem(dict: dataObjectFromPlist))
}
}
func numberOfItem() -> Int {
return allItems.count
}
func explore(at index: IndexPath) -> ExploreItem {
return allItems[index.item]
}
fileprivate func loadData() -> [String: AnyObject]? {
var resourceFileDictionary: [String: AnyObject]?
if let path = Bundle.main.path(forResource: "Info", ofType: "plist") {
if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
resourceFileDictionary = dict
}
}
return resourceFileDictionary
}
}
然后在我的 FirstViewController
中,我可以使用
从 ExploreStore
class 获取数据
exploreStore.fetch()
我的 UIViewController
代码是
class FirstViewController: UIViewController {
var exploreStore: ExploreStore!
override func viewDidLoad() {
super.viewDidLoad()
exploreStore.fetch()
print(exploreStore.allItems[0].data)
}
}
此处 exploreStore.allItems[0].data
将打印我的整个 info.plist
文件。
您可以使用 THIS 演示项目自行尝试并检查这是否是正确的行为。
编辑
您需要更新 didFinishLaunchingWithOptions
方法,例如:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
setupDefaultColors()
let exploreStoryBoard = UIStoryboard(name: "Explore", bundle:nil)
let navigationController = exploreStoryBoard.instantiateViewController(withIdentifier: "ExploreViewControllerNavigation") as! UINavigationController
if let exploreViewController = navigationController.children.first as? ExploreViewController {
exploreViewController.store = ExploreStore()
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = exploreViewController
self.window?.makeKeyAndVisible()
}
return true
}
并且您还需要更新 ExploreStore
class 如下所示:
class ExploreStore {
var allItems = [ExploreItem]()
func fetch() {
if let dataObjectFromPlist = loadData() {
allItems.append(ExploreItem(dict: dataObjectFromPlist))
}
}
func numberOfItem() -> Int {
return allItems.count
}
func explore(at index: IndexPath) -> ExploreItem {
return allItems[index.item]
}
fileprivate func loadData() -> [String: AnyObject]? {
var resourceFileDictionary: [String: AnyObject]?
if let path = Bundle.main.path(forResource: "ExploreData", ofType: "plist") {
if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
resourceFileDictionary = dict
}
}
return resourceFileDictionary
}
}
因为从 plist
你会得到 Dictionary<String, AnyObject>
类型的对象。
而且您仍然无法从 plist
文件中获取数据,因为它已添加到子文件夹中。所以你需要先为你的 plist
.
找到正确的路径
您还需要为导航控制器和标签栏控制器分配各自的标识符。
Here 是您的演示项目。
我想在我的应用程序中的应用程序委托中实现依赖倒置,因为我的 rootController
是我的 UITabBarController
但是当我想尝试它时出现错误
Fatal error: Unexpectedly found nil while unwrapping optional value
这是我在 appDelagate 中的代码
let exploreStore = ExploreStore()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let rootController = (window?.rootViewController as? UITabBarController)?.children.first as? ExploreViewController
// Inject Data
rootController?.exploreStore = exploreStore
return true
}
这是我的探索class
class ExploreStore {
fileprivate var allItems = [ExploreItem]()
func fetch() {
for data in loadData() {
allItems.append(ExploreItem(dict: data))
}
}
func numberOfItem() -> Int {
return allItems.count
}
func explore(at index: IndexPath) -> ExploreItem {
return allItems[index.item]
}
fileprivate func loadData() -> [[String: AnyObject]] {
guard
let path = Bundle.main.path(forResource: "ExploreData", ofType: "plist"),
let items = NSArray(contentsOfFile: path)
else { return [[:]] }
return items as! [[String: AnyObject]]
}
}
这是我的 exlporeViewController
var exploreStore: ExploreStore!
override func viewDidLoad() {
super.viewDidLoad()
// This is where the error found nil
exploreStore.fetch()
}
实际上,如果我不使用依赖倒置,代码就可以工作,比如我的探索视图控制器不使用像这样的强制解包
var exploreStore = ExploreStore()
但是由于我想获得知识并学习使用依赖倒置的 S.O.L.I.D 原则,所以我想坚持这个原则。
如果我正确理解了你的问题,你想在 AppDelegate
class 处初始化你的 class,然后你想将它传递给 UITabBarController
的第一个 children
为此,您需要对 didFinishLaunchingWithOptions
方法进行一些修改,如下所示:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "tabBar")
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = vc
let myTabBar = self.window?.rootViewController as! UITabBarController
let firstViewController = myTabBar.children.first as? FirstViewController
firstViewController?.exploreStore = exploreStore
self.window?.makeKeyAndVisible()
return true
}
我在这里做了一些修改,因为我正在从 Bundle
中检索 Info.plist
,而您的 ExploreStore
看起来像:
class ExploreStore {
var allItems = [ExploreItem]()
func fetch() {
if let dataObjectFromPlist = loadData() {
allItems.append(ExploreItem(dict: dataObjectFromPlist))
}
}
func numberOfItem() -> Int {
return allItems.count
}
func explore(at index: IndexPath) -> ExploreItem {
return allItems[index.item]
}
fileprivate func loadData() -> [String: AnyObject]? {
var resourceFileDictionary: [String: AnyObject]?
if let path = Bundle.main.path(forResource: "Info", ofType: "plist") {
if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
resourceFileDictionary = dict
}
}
return resourceFileDictionary
}
}
然后在我的 FirstViewController
中,我可以使用
ExploreStore
class 获取数据
exploreStore.fetch()
我的 UIViewController
代码是
class FirstViewController: UIViewController {
var exploreStore: ExploreStore!
override func viewDidLoad() {
super.viewDidLoad()
exploreStore.fetch()
print(exploreStore.allItems[0].data)
}
}
此处 exploreStore.allItems[0].data
将打印我的整个 info.plist
文件。
您可以使用 THIS 演示项目自行尝试并检查这是否是正确的行为。
编辑
您需要更新 didFinishLaunchingWithOptions
方法,例如:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
setupDefaultColors()
let exploreStoryBoard = UIStoryboard(name: "Explore", bundle:nil)
let navigationController = exploreStoryBoard.instantiateViewController(withIdentifier: "ExploreViewControllerNavigation") as! UINavigationController
if let exploreViewController = navigationController.children.first as? ExploreViewController {
exploreViewController.store = ExploreStore()
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = exploreViewController
self.window?.makeKeyAndVisible()
}
return true
}
并且您还需要更新 ExploreStore
class 如下所示:
class ExploreStore {
var allItems = [ExploreItem]()
func fetch() {
if let dataObjectFromPlist = loadData() {
allItems.append(ExploreItem(dict: dataObjectFromPlist))
}
}
func numberOfItem() -> Int {
return allItems.count
}
func explore(at index: IndexPath) -> ExploreItem {
return allItems[index.item]
}
fileprivate func loadData() -> [String: AnyObject]? {
var resourceFileDictionary: [String: AnyObject]?
if let path = Bundle.main.path(forResource: "ExploreData", ofType: "plist") {
if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
resourceFileDictionary = dict
}
}
return resourceFileDictionary
}
}
因为从 plist
你会得到 Dictionary<String, AnyObject>
类型的对象。
而且您仍然无法从 plist
文件中获取数据,因为它已添加到子文件夹中。所以你需要先为你的 plist
.
您还需要为导航控制器和标签栏控制器分配各自的标识符。
Here 是您的演示项目。