为什么从coredata返回的数据在swift中的tableview滚动后变为nil?

时间:2016-12-13 14:25:02

标签: ios swift uitableview

更新注意:这个问题似乎与How can I fix crash when tap to select row after scrolling the tableview?没有重复,因为我的问题是关于我的一个CoreData对象在表滚动后nil。但是,只要应用程序因topic对象的nil而崩溃,单元就会返回数据。 基本上这部分失败了:let estimatedSkills = topic.topicEstimatedSkilllet topic = self.topics!.object(at: indexPath.row) as! Topic,即使一开始,调试打印显示对象不是nil,滚动后,self.topic变为零。

原始 我希望你能帮助我,因为我是Swift和苹果开发的初学者。

我继承了一个没有用的ios应用程序,因为从我们的服务器返回的数据结构发生了变化。以前的开发人员无法联系到,所以我想知道在这里做什么。最重要的是,在开发过程中,苹果强迫我更新到swift3和xcode8,改变了游戏规则,使一切变得更加混乱。

所以基本上我有一个tableview,它从从CoreData获取的对象中获取数据。它在启动时填充单元格,但滚动会使对象中的数据返回nil。

有问题的对象名为Topic 并且代码在以下位置失败:

    //TODO - this sometimes gives nil but shouldnt
    if let estimatedSkills = topic.topicEstimatedSkill {
        value = estimatedSkills.doubleValue * Double(cell.starViewContainer.subviews.count);
        debugPrint("success getting estimated skills!" , indexPath, estimatedSkills)
    } else {
        debugPrint("Didnt get estimated skills :(")
    }

然后当我回到原始单元格时,其中的信息也消失了。

这是tableview函数:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: Identifiers.cells.superTrainer.rawValue, for: indexPath) as! SuperTrainerOverViewCell;
        cell.backgroundColor = UIColor.clear;

        let topic = self.topics!.object(at: indexPath.row) as! Topic;

        cell.titleLabel.text = topic.topicName;

        var value = 0.0

        //TODO - this sometimes gives nil but shouldnt
        if let estimatedSkills = topic.topicEstimatedSkill {
            value = estimatedSkills.doubleValue * Double(cell.starViewContainer.subviews.count);
            debugPrint("success getting estimated skills!" , indexPath, estimatedSkills)
        } else {
            debugPrint("Didnt get estimated skills :(")
        }

        let wholeStar = Int(floor(value));

        for index in (0..<wholeStar) {
            let imageView = cell.starViewContainer.subviews[index] as! UIImageView;
            imageView.image = UIImage(named: "icon-star-100");
        }

        if(wholeStar < cell.starViewContainer.subviews.count) {
            let decimal = fmod(value, 1);
            var image = UIImage(named: "icon-star-100");

            if(decimal < 0.15) {
               image = UIImage(named: "icon-star-0");

            }else if(decimal >= 0.15 && decimal < 0.4) {
                image = UIImage(named: "icon-star-25");

            }else if(decimal >= 0.4 && decimal < 0.65) {
                image = UIImage(named: "icon-star-50");

            }else if(decimal >= 0.65 && decimal < 0.80) {
                image = UIImage(named: "icon-star-75");
            }

            let imageView = cell.starViewContainer.subviews[wholeStar] as! UIImageView;
            imageView.image = image;
        }

        return cell;
    }

该类有一个topic属性,设置为:

override func viewDidLoad() {
    self.navigationItem.title = "SUPERTRAINER".localized;
    self.tableView.backgroundColor = UIColor.init(rgba: hexColors.gray.rawValue);

    if(Utility.isConnectedToNetwork()) {
        self.getSuperTrainerData(student: self.student!);
    } else {
        self.topics = Topic.getTopics(student: self.student!, context:  NetworkService.sharedInstance.coreDataHandler.context!);
        Utility.showNoConnectionAlertView();
    }

    self.tableView.tableFooterView = UIView(frame: CGRect.zero);
}


// MARK: Custom Methods
func getSuperTrainerData(student: Student) {
    _ = SwiftSpinner.show("FETCHING_DATA".localized);

    Utility.backgroundThread( background: { () -> Void in
        self.networkService.getSuperTrainerData(student: student) { (complet, returnDics, errorMessage) -> Void in

            if(complet) {
                self.removeSuperTrainerData();
                self.createSuperTrainerData(dics: returnDics!);
            } else {
                Utility.showAlertView(title: "LOGIN_FAILED_TITLE".localized, message: errorMessage);
            }

        }
    }) { () -> Void in
        self.networkService.coreDataHandler.saveContext();
        self.topics = Topic.getTopics(student: self.student!, context:self.networkService.coreDataHandler.context!)!;
        SwiftSpinner.hide();
        self.tableView.reloadData();
    }
}

有人有什么想法吗? :)我现在很快就能迅速弄清楚所有这一切,所以任何帮助都非常感激!

1 个答案:

答案 0 :(得分:0)

我最终发现它实际上是一个线程(并发)问题。

基本上,在getSuperTrainerData函数中,应该创建一堆对象。鉴于他们一直返回nill,ViewController当然会拒绝创建行..但是,两次输入相同的视图会给应用程序时间来存储和缓存从网络调用返回的对象。

我打电话给Utility.backgroundThread,这只是dispatchQueue的包装。这意味着networkService被放置在后台线程中。但是在networkService里面有一个urlSession调用。调用服务器创建自己的后台线程,所以即使我通过后台线程调用我的服务器调用,它创建了自己的后台线程,并且从未返回主调用。

我使用的解决方案只是进行服务器调用,并将后台对象创建调用放在完成处理程序中,如下所示:

    self.networkService.getSuperTrainerData(student: student) { (complet, returnDics, errorMessage) -> Void in
        if(complet) {
            DispatchQueue.global(qos: DispatchQoS.userInitiated.qosClass ).async {
                self.removeSuperTrainerData();
                self.createSuperTrainerData(dics: returnDics!);

                DispatchQueue.main.async( execute: {
                    print("main queue without completion")
                    self.networkService.coreDataHandler.saveContext();
                    self.topics = Topic.getTopics(student: self.student!, context:self.networkService.coreDataHandler.context!)!;
                    SwiftSpinner.hide();
                    self.tableView.reloadData();
                })
            }

        } else {
            Utility.showAlertView(title: "LOGIN_FAILED_TITLE".localized, message: errorMessage);
        }
    }

希望这有助于某人:)