Question about changes with Xcode, Cocoa and Swift, dealing with Concurrency


#1

Since I’m new to programming, and new to Cocoa/Swift programming using Xcode, I’m not sure of the changes made over the years. The book suggests that the scattered program will run on a single thread (before the modifications in this chapter). However, it appears to me that now Xcode automatically runs multithreads. Here are my initial results from the Time Profiler of Instruments (before the chapter modifications):

As you can see in the top left, there is a Main Thread with _dispatch_worker_thread3 under that, and so on. This implies to me that Scattered is running multithreads without my having to do a thing. Plus the setup of the Instruments screen is now different so some of the things pointed out in this chapter no longer apply, or I should say, I haven’t found them yet.

I continued with the chapter and added the processingQueue code throughout as suggested, with corrections for Swift 4.1. Interesting results. Before the changes were made the time interval was 0.6 seconds:

And you can see my text fields and button are still in place.

After making the modifications suggested in the chapter, with the Swift 4.1 changes, the program takes from 5.2 up to 6.8 seconds to run. So it is slower. I’m assuming this is because Xcode already had it multithreading, and the added code made it more complicated/cumbersome.

And you can see in the bottom right, the text fields and button are covered. The blue outline of a text field can be made out, but it definitely isn’t correct. I can click where the button was and it still clicks though, through the images. I’m not sure how the added code caused this.

So, am I correct thinking that Xcode, and/or Cocoa, and/or Swift 4.1 now automatically sets up multithreading? And if so, other than learning the Chapter, there isn’t much to do here but move on.

Here is the pertinent code in ViewController:

	//-----------------------------------

let processingQueue: OperationQueue = {
	let result = OperationQueue()
	result.maxConcurrentOperationCount = 4
	return result
}()

//-----------------------------------

...

func addImagesFromFolderURL(folderURL: URL) {
	processingQueue.addOperation ({
	
		let t0 = Date.timeIntervalSinceReferenceDate
		
		let fileManager = FileManager()
		let directoryEnumerator = fileManager.enumerator(at: folderURL,
														 includingPropertiesForKeys: nil,
														 options: [],
														 errorHandler: nil)!
		
		while let url = directoryEnumerator.nextObject() as? URL {
			// Skip directories
			var urlResource: URLResourceValues!
			do {
				urlResource =
					try url.resourceValues(forKeys: [.isDirectoryKey])
			} catch {
				print("error checking whether URL is directory: \(error)")
				continue
			}
			guard !urlResource.isDirectory! else { continue }
			
			self.processingQueue.addOperation({
				guard let image = NSImage(contentsOf: url) else { return } // continue
				
				let thumbImage = self.thumbImageFromImage(image: image)
				
				OperationQueue.main.addOperation({
					self.presentImage(image: thumbImage)
					// find file name and assign to filename
					self.filename = url.deletingPathExtension().lastPathComponent as String
				
					let t1 = Date.timeIntervalSinceReferenceDate
					let interval = t1 - t0
					self.text = String(format: "%0.1fs", interval)
				})
			})
		}
	})
}

//--------------------------------------------------------------------------------------------------------------

#2

Before moving on, if you haven’t already done so, have a look at the Dispatch Queues in GCD, the framework that enables programmers to take advantage of the multiple cores available on Apple computing platforms.

You can also find more information on GCD in the manual pages, by entering the command man dispatch on the command line.


#3

Thanks! I’ll do that!