我的主视图控制器是通过观察何时发布通知并被多次调用来加载的

My main view controller is loaded by observing when a notification is posted and is being called multiple times

我从显示 gif 的加载屏幕启动我的应用程序,同时在后台加载用户位置和适当的事件

我试图调用视图控制器上的 deinit() 方法来删​​除观察者,并删除视图控制器作为位置更新的委托,以确保位置更新和随后启动的主屏幕仅调用一次

我的实现可能相当复杂,但我会尽力确保它清晰易懂。我从我的 AppDelegate 文件中调用 startApplication() 来启动加载屏幕

AppDelegate



func startApplication(animated: Bool, pushCategory: String?) {

    let mainNC = self.window?.rootViewController as! UINavigationController
    let storyboard = UIStoryboard.init(name: "Main", bundle: nil)

    let homeVC = storyboard.instantiateViewController(withIdentifier: String(describing: LoadViewController.self)) as! LoadViewController
    if pushCategory != nil {
        homeVC.pushCategory = pushCategory!
    }
    mainNC.pushViewController(homeVC, animated: false)

}

在我的 LoadVC 中,我正在加载 gif,然后在从数据库中检索到本地事件时添加一个观察者

   override func viewDidLoad() {
    super.viewDidLoad()

    holdView.frame = UIScreen.main.bounds
    holdView.loadGif(name: "ANO_loading_gif")
    holdView.contentMode = .scaleAspectFill

    showMessageView.layer.cornerRadius = 10.0
    showMessageView.clipsToBounds = true

    locationService.delegate = self

    if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {

        changeLocationMessage()
    }
}

 override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    //get notified when events have been downloaded from API
    NotificationCenter.default.addObserver(self, selector: #selector(receivedCloseEvents(_:)), name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
}

找到位置后,将调用 LocationService 方法对服务器进行 REST API 调用

func tracingLocation(currentLocation: CLLocation) {

     GlobalService.sharedInstance().updateUserLocation(currentLocation)
}

然后在我的 'GlobalService' 文件中,如果调用成功,一系列函数将 运行 在后台运行,因为它们很耗时

  func updateUserLocation(_ UserLocation: CLLocation) {

    g_userCLLocation = UserLocation
    g_userCurrentLocation = nil

    // get user location to update Hub name
    for location in g_aryLocations {
        let coreLocation = CLLocation(latitude: location.location_latitude!, longitude: location.location_longitude!)
        let fDistance = coreLocation.distance(from: UserLocation)

        // check event distance
        if fDistance < Double(Constants.Numbers.EVENT_LIST_DISTANCE) {
            g_userCurrentLocation = location
            break
        }
    }

    if g_userCLLocation != nil {

        // get all valid events
        WebService.sharedInstance().getActiveEvents(UserLatitude: UserLocation.coordinate.latitude, UserLongitude: UserLocation.coordinate.longitude)
        { (aryEvents, error) in
            if let error = error {
                //show issue with network & load from archives if accessible
            } else {

             //set events to the empty array
             //move these time expensive tasks to background thread
                self.group.enter()

                //execute event recovery on background thread
                DispatchQueue.global(qos: .background).async {
                    self.g_aryEvents = aryEvents!
                    self.getUsersWithinRange(aryEvents!)
                    self.updateUserEvent(nil)
                }

            }
        }

    }

}

UpdateUserEvent() 通过计算找到用户当前所在的事件,然后一旦找到它就会发布通知以通知 LoadVC 它应该移动到 mainVC

//once user event has been updated send to
private func updateUserEvent(_ liveKey: Int?) {
    //operations should happen on the background thread
    //ensure live key is not nil
    if liveKey != nil {
        //clone events array
        var swappableEventsArr = g_aryEvents
        var eIndex = 0
        //30 min start time
        let bufferTime = Constants.Numbers.EVENT_LIVE_TIME
        var potentialLiveEvent: EventObj?
        //search for event with matching event ID to live key
        for event in swappableEventsArr {
            eIndex += 1
            if event.event_id! == liveKey {
                potentialLiveEvent = event
                break
            }
        }
        //set live event to true
        guard let actualEvent = potentialLiveEvent else { return }


        if actualEvent.event_time!.timeIntervalSinceNow <= Double(bufferTime) || actualEvent.event_user_id! == 1 {
            actualEvent.event_is_live = true
        }
        //set former live event to false
        if swappableEventsArr[0].event_id! != actualEvent.event_id! {
            swappableEventsArr[0].event_is_live = false
        }
        //swap events index
        swappableEventsArr.swapAt(0,eIndex - 1)
        //remove all previous events
        g_aryEvents.removeAll()
        //set array to new order with live event in front
        g_aryEvents = swappableEventsArr
    }

    //once value has been achieved exit thread
    //set live event
    self.g_aryEvents[0].event_is_live = true
    self.g_objLiveEvent = self.g_aryEvents[0]

    //this call can occur on the background thread
    WebService.sharedInstance().updateUserEvent(UserEvent: g_objLiveEvent!.event_id!)
    { (result, error) in
        if let error = error {
            print(error)

        } else {
            print(result)
        }
    }
     //move back to main thread
    self.group.leave()
    //notify main thread that events have finished loading
    self.group.notify(queue: .main, execute: {

    //call main VC immediately once done prcoessing event ordering
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
     })
}

最后,选择器方法被调用,告诉 LoadVC 移动到 MainVC

   @objc func receivedCloseEvents(_ notification:Notification) {

    locationService.stopUpdatingLocation()

    let g_events = GlobalService.sharedInstance().g_aryEvents

    self.events = g_events
    //send events to Main VC once done

        let mainVC = self.storyboard?.instantiateViewController(withIdentifier: String(describing: MainViewController.self)) as! MainViewController
        mainVC.arrEvents = self.events

        if self.pushCategory != nil {
            mainVC.pushCategory = self.pushCategory!
        }

        self.navigationController?.pushViewController(mainVC, animated: true)
    }

然后我调用 deinit() 方法删除观察者并取消分配 LoadVC 作为 LocationService 委托

 deinit {
    //remove location service delegate
    locationService.delegate = nil
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
}

通常它的行为符合预期,但有时它会多次推送 MainVC。我还放了一个 属性 观察者 g_usercllocation 并且在 didSet 中它打印了 "location called" 两次。我不确定是不是因为我正在使用 requestLocation() 作为位置,因为我认为它只会在找到位置时被调用一次。

两个可能的问题:

1- 为通知观察不止一次。

2- 多次发布一个通知。

由于您需要对 receivedCloseEvents 方法进行一次分派,因此请将以下行放在 receivedCloseEvents 方法的开头。

NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)

因此,一旦收到通知,它就会取消注册,直到视图再次出现并重新注册该事件。