更新注意:这个问题似乎与How can I fix crash when tap to select row after scrolling the tableview?没有重复,因为我的问题是关于我的一个CoreData对象在表滚动后nil。但是,只要应用程序因topic
对象的nil而崩溃,单元就会返回数据。
基本上这部分失败了:let estimatedSkills = topic.topicEstimatedSkill
和let 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();
}
}
有人有什么想法吗? :)我现在很快就能迅速弄清楚所有这一切,所以任何帮助都非常感激!
答案 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);
}
}
希望这有助于某人:)