问题
I have an array of PFFiles downloaded from Parse, and I am trying to convert them in to an array of NSData (imageDataArray) in order to save them to Core Data. The only problem I seem to be having now is that the elements of imageDataArray are being added in the wrong order, which means the wrong image is found when I search through Core Data.
I found this question (Stack Overflow Question) which seems to explain the reason for the problem as being that the block is completing the tasks at different times, therefore the order of the array is based on whichever is finished first. They then suggest using Grand Central Dispatch, but it is all in Obj C and, as a newbie to coding, I am struggling to convert it to Swift for my project.
Could you please explain to me how I would use GCD (or any other method) to create the imageDataArray in the same order as the original PFFile array?
override func viewDidLoad() {
super.viewDidLoad()
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let dealsQuery = PFQuery(className: ("Deals"))
dealsQuery.orderByAscending("TrailId")
dealsQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
self.trailID.removeAll(keepCapacity: true)
self.trailStep.removeAll(keepCapacity: true)
self.dealNumber.removeAll(keepCapacity: true)
self.imageFile.removeAll(keepCapacity: true)
for object in objects {
self.trailID.append(object["TrailID"] as! Int)
self.trailStep.append(object["TrailStep"] as! Int)
self.dealNumber.append(object["dealNumber"] as! Int)
self.imageFile.append(object["dealImage"] as! PFFile!)
}
}
var counter = 0
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
weak var aBlockSelf = self
let image = UIImage(data: imageData!)
aBlockSelf!.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
if counter == self.trailStep.count - 1{
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.trailStep[0])
print(self.dealNumber[0])
let image = UIImage(data: self.imageDataArray[0])
self.imageView.image = image
} else {counter++}
}
})
Sorry for posting all of my code here. I guess the issue is at the bottom, but as a beginner I thought I might have made a mistake somewhere else so thought I would be best posting it all.
Thanks a lot for your help.
Update 1
I have tried adding a prefilled array (imageArray) and trying to put the data in to that, but it still comes out random when searching for trailStep and dealNumber. What am I missing? Thanks
var i = 0
var imageArray = [NSData!](count: self.trailStep.count, repeatedValue: nil)
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
//weak var aBlockSelf = self
let image = UIImage(data: imageData!)
imageArray[i] = imageData!
if i == self.trailStep.count - 1{
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.trailStep[3])
print(self.dealNumber[3])
let image = UIImage(data: imageArray[3])
self.imageView.image = image
} else {i++}
}
})
Update 2
I have been researching and having a play around with GCD and serial dispatches, and here is where I am at the moment. I think I am taking a far too simplistic view of GCD but can't quite get my head around how it works and how it can get my block to add to imageDataArray in the right order.
var counter = 0
var imageArray = [NSData!](count: self.trailStep.count, repeatedValue: nil)
weak var aBlockSelf = self
var serialQueue = dispatch_queue_create("serial", nil)
for file in self.imageFile {
let dealImage = file
var imageDataArray = [NSData!](count: self.imageFile.count, repeatedValue: nil)
dispatch_async(serialQueue, { () -> Void in
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let imageIndex = self.imageFile.indexOf(file)
let image = UIImage(data: imageData!)
imageDataArray.insert(imageData!, atIndex: imageIndex!)
if counter == self.trailStep.count - 1{
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(imageDataArray.count)
print(self.trailStep[0])
print(self.dealNumber[0])
let image = UIImage(data: imageDataArray[4])
self.imageView.image = image
} else {counter++}
return
}
})
This code is returning an error of "unexpectedly found nil while unwrapping an Optional value" on the line " let image = UIImage(data: imageDataArray[4])". This is the same error that I was getting before I tried to implement the GCD...
回答1:
This should be fairly simple. Although GCD dispatch group would be ideal choice, you can solutionize this by keeping a counter of for loop.
In your completion block, deduce the index of the object you are currently inside the for loop using the parent array. In your final image array, add object at the index captured by this index.
EDIT: Post OP question - here is a quick sneak peek of how I would like it to be:
weak var aBlockSelf = self
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let imageIndex = self.imageFile.indexOf(file)
let image = UIImage(data: imageData!)
aBlockSelf.imageDataArray.insert(imageData!, atIndex: imageIndex)
aBlockSelf.imagesArray.insert(image!, atIndex: imageIndex)
As a side not, do not declare self weak reference in the for loop. You should declare it outside.
回答2:
You don't need to use GCD for this; you just need an array (or map, if you want to associate each image with its filename) to stick each image in when it's retrieved. In other words, if you have files "f1", "f2", "f3", and "f4", in that order, create an array to hold the retrieved images, and insert them at whatever index corresponds to their filename's index in the list of filenames.
来源:https://stackoverflow.com/questions/33429044/loop-within-block-appending-to-array-in-the-wrong-order-swift-2-0