gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 12:49:58 24 4
gpt4 key购买 nike

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

原文:我希望你能帮助我,因为我是 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 :(")
}

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

这是表格 View 函数:

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;
}

该类有一个主题属性,其设置为:

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();
}
}

大家有什么想法吗? :) 我对 swift 还太陌生,现在无法快速弄清楚所有这些,所以非常感谢任何帮助!

最佳答案

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

基本上,在 getSuperTrainerData 函数中,应该创建一堆对象。鉴于它们始终返回 null,ViewController 当然会拒绝创建行。但是,两次进入同一 View 将使应用程序有时间存储和缓存从网络调用返回的对象。

我调用了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);
}
}

希望这对某人有帮助:)

关于ios - 为什么在swift中tableview滚动后从coredata返回的数据变成nil?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41123465/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com