I have a number of NSOperations which create some data asynchronously. I want to collect all of the results into one array. Because I'm accessing the array on multiple different threads, I've put locking around the array.
The NSOperationQueue is appending the data to the array but the results seem to miss some of the data objects. The results seem to change each time I run it.
I've created a simplified example project that recreates the issue. The code is in Swift but I don't think this is Swift-specific.
import UIKit
class ViewController: UIViewController {
let queue = NSOperationQueue()
var bucket = [String]()
override func viewDidLoad() {
super.viewDidLoad()
queue.addObserver(self, forKeyPath: "operations", options: NSKeyValueObservingOptions.New, context: nil)
for _ in 0..<10 {
queue.addOperation(NSBlockOperation {
// Let's pretend that creating the "fish" string is actually potentially
// expensive and that's why we're doing it in an NSOperation.
let fish = "fish"
objc_sync_enter(self.bucket)
self.bucket.append(fish)
let fishCount = self.bucket.count
print("Bucket contains \(fishCount) fish" + ((fishCount != 1) ? "es" : ""))
objc_sync_exit(self.bucket)
})
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let keyPath = keyPath {
if let object = object as? NSOperationQueue {
if object == queue && keyPath == "operations" {
if queue.operationCount == 0 {
objc_sync_enter(self.bucket)
let fishCount = bucket.count
print("Bucket finally contains \(fishCount) fish" + ((fishCount != 1) ? "es" : ""))
objc_sync_exit(self.bucket)
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
}
}
}
The results vary but are often something like this:
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 2 fishes
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 3 fishes
Also, sometimes the code crashed with an EXC_BAD_ACCESS on the line self.bucket.append(fish)
In addition, the line print("Bucket finally contains \(fishCount) fish" + ((fishCount != 1) ? "es" : "")) in observeValueForKeyPath never gets called. I'm not sure if this is a separate issue or not.
You should look at subclassing NSOperation, since it is an abstract class.
See this Stackoverflow question for subclassing.
With that in mind I would suggest that you have an identifier property on each operation instance so that you can keep track of your operations, that way you can tell when all of your operations have finished. You might also consider pulling this code out of your view controller class and creating a class to handle your fish Plus it will help you with encapsulation further down the road when say you are no longer interested in fish but cats :)
The Concurrency Programming Guide is really good at explaining the basics of asynchronous application design.
The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task. Because it is abstract, you do not use this class directly but instead subclass or use one of the system-defined subclasses (NSInvocationOperation or NSBlockOperation) to perform the actual task. Despite being abstract, the base implementation of NSOperation does include significant logic to coordinate the safe execution of your task. The presence of this built-in logic allows you to focus on the actual implementation of your task, rather than on the glue code needed to ensure it works correctly with other system objects.
Related
i'd could use your advice on this one
i restructured my whole app, to use coreData so i'd be able to save the Main Class of my app: "FooBar"
i also transfered my complete business logic into that NSManagedObject subclass FooBar()
that's why i needed to to use some kind of convenience init, to start my own methods for calculating values and so on
here's my class definition:
Import Foundation
import CoreData
import UIKit
#objc(FooBar)
public class FooBar: NSManagedObject {
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext // used for other CD interactions inside this class
var member1 = Int()
var member2 = Double()
var member3:Double = 0
var fooPack:[FooPacks] = []
...
convenience init( param1: Int16
, param2: String?
, param3: Float
, param4: String?
, param5: Float
, fooPack: NSSet
, entity: NSEntityDescription
, context: NSManagedObjectContext?
) {
self.init(entity: entity, insertInto: context)
self.param1 = param1
self.param2 = param2
self.param3 = param3
self.param4 = param4
self.param5 = param5
self.addToUsedfooBarPacks(fooBarPack)
self.build()
}
func build() {
// i do something
}
func method1() {
// i do something
}
when i initialize my FooBar class inside a viewController i do this by:
self.fooBar = FooBar.init(param1: var1,
param2: var2,
iparam3: var3,
param4: var4,
param5: var5,
fooPack: var6,
entity: FooEntity,
context: context)
when ever i modify any instance of this class i do this by:
self.fooBar.member2 = newValue
self.fooBar.build()
and later at a specific point (in multiple VC's) i then call to save that Instance to CoreData:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
try context.save()
}catch{
print("\(error)")
}
with this approach i often have to deal with bugs and unexpected behaviours like:
the do block gets executed successfully, but the object has NOT been saved!?
i often hand over FooBar instances to a different ViewController and i try to save them there,
but this does not seem to be the right way to do update previous fetched objects
in my opinion, this has to do with the way i handle the context variables..
and exactly this is why i am reaching out for your advice..
What is the optimal way to handle such a situation, where i want to modify and save instances within multiple different ViewControllers?
i already learned a lot new stuff about CoreData in the last months, but i seem to miss something important here..
please help me out guys :)
You're using persistentContainer, it helps you setup coredata stack. persistentContainer has two types of context: viewContext and background contexts
With this setup we always use viewContext to fetch data to display on UI.
And use a background context to make changes, update storage...
What is the kind of context you passed in init method?
You should execute context.save() on exactly the context that you used to make changes (you passed to init). In this case you shouldn't pass viewContext to init and use viewContext to call save()
How to update and sync changes between multiple ViewControllers?
The view controller that makes changes should call save as well. And you can use delegation pattern to delegate/broadcast changes to other view controllers...
I'm currently whipping up a first Swift side project. What I'm trying to achieve right now, is to have my service layer listen for updates to my Firebase database, and then update the view controllers.
The simple Ray Wenderlich tutorial I am on has the ViewController itself creating a FIRDatabase reference, and observing directly which feels far too coupled.
I'd rather my service layer listen for this, and then dole it out to those necessary. There is no KVO in Swift, correct?
What would be an appropriate Swift norm for updating VC's from your service layer? Are Notifications my only recourse here?
EDIT: Is this a desirable thing to do with Firebase. I've not worked with it before. On the plus side, the database will be decoupled from various VC's and be central to the Service layer which is great. On the downside, even though a user is not in a particular view, the service layer will always be observing all sort of changes that perhaps aren't relevant to where the user currently is.
Firebase databases are really cool for what you are trying to achieve. Actually, KVO works on classes that are inherited from NSObject, just the same as it works in Objective-C. Nevertheless, you have plenty other options to update your view controller.
The first one is to use callbacks.
This is a very swifty way to do it. Everytime that the database is updated, you could call a callback that is stored in your service layer. The view controller sets up the callback in the service layer.
For example:
Your service layer:
class ServiceLayer {
var callback: ((Model) -> ())!
func listenForUpdates() {
let ref = FIRDatabase.database().reference(withPath: "path")
ref.observe(FIRDataEventType.value, with: { snapshot in
guard let modelList = snapshot.value as? [String : AnyObject], let model = modelList["key"] as? Model else {
return
}
callback(model)
})
}
And in your view controller, if you have an instance of the serviceLayer or something similar, you can do the following:
let serviceLayer = ServiceLayer()
serviceLayer.callback = { model in
label.text = model.name
//Update or do whatever you want with the model
}
The second way, and my favorite, is to use Rx.
Its the main library for Reactive programming in swift (RxSwift) and gives you many more options. It's pretty hard to get the grip on it at first but it's totally worth it.
The previous example done with Rx would be the following:
func listenForChanges() -> Observable<Model> {
return Observable.create { subscriber in
let ref = FIRDatabase.database().reference(withPath: "path")
ref.observe(FIRDataEventType.value, with: { snapshot in
guard let modelList = snapshot.value as? [String : AnyObject], let model = modelList["key"] as? Model else {
return
}
subscriber.onNext(model)
})
return Disposables.create()
}
}
And in the view controller:
let disposeBag = DisposeBag()
let serviceLayer = ServiceLayer()
serviceLayer.listenForChanges()
.do(
onNext: { [weak self] model in
label.text = model.name
}
)
.subscribe()
.addDisposableTo(disposeBag)
The differences are not so notorious here, but Rx brings a new paradigm that is really useful in swift.
If you want to further information on Rx, look in the following link http://reactivex.io/
I am trying to make a dictionary with the properties of a class of mine.
class SomeClass() {
var someString = "Hello, stackoverflow"
var someInt = 42 // The answer to life, the universe and everything
var someBool = true
func objectToDict() -> [String: String] {
var dict = [String: String]()
let reflection = Mirror(reflecting: self)
for child in reflection.children {
if let key = child.label {
dict[key] = child.value as? AnyObject
}
return dict
}
}
but objectToDict() is very slow. Is there a way to speed this up, or may be another approach to add the property values to a Dictionary?
I do not agree with most other users. Using reflection results less code, which means less time to develop, maintain, and test your product. With a well written library like EVReflection you don't need to worry about things behind the scene too much.
However, if performance is going to be a concern, do NOT use reflection based approaches at all. I'd say it's never really a problem in front-end development for me, especially in iOS, but it cannot be ignored in back-end development.
To see how slow it can be, I ran some test in Xcode. I'll write a blog about it, but generally speaking, getting Mirror is not the worst part (plus it may be possible to catch property list in memory), so replacing it with objc runtime wouldn't change the situation too much. In the other hand, setValue(_, forKey) is surprisingly slow. Considering that in real life you also need to perform tasks like checking dynamicType and so on, using the dynamic approach surely will make it 100+ times slower, which won't be acceptable for server development.
- Looping 1,000,000 times for a class with 1 `Int` property
- Getting mirror: 1.52
- Looping throw mirror and set `child.value`: 3.3
- Looping throw mirror and set `42`: 3.27
- Setting value `42`: 0.05
Again, for iOS I'll keep using it to save my time. Hopefully end customers won't care about whether it's 0.005 seconds or 0.0005 seconds.
Not only is that slow, it's also not a good idea: mirroring is for debug introspection only. You should instead construct the dictionary yourself. This ensures that you have the flexibility to store all the data in exactly the right way, and also decouples your Swift property names from the keys of the dictionary you're generating.
class SomeClass {
var someString = "Hello, stackoverflow"
var someInt = 42 // The answer to life, the universe and everything
var someBool = true
func objectToDict() -> [String: AnyObject] {
return ["someString": someString, "someInt": someInt, "someBool": someBool]
}
}
I'm trying to parse a large JSON file (approx: 1000 rows containg a tuple with 8 strings) and display this in a UITableView. What I already have is working but I am looking for a more efficient way of displaying them.
At the moment my code looks likes this:
public func GET(request: String, callback: (result: JSON?, response: NSHTTPURLResponse?, error: NSError?) -> Void) {
let session = NSURLSession.sharedSession()
let url = NSURL(string : "SOMEURL")
let task = session.dataTaskWithURL(url!){
(data, response, error) -> Void in
if error != nil {
callback(result: nil, response: response as? NSHTTPURLResponse, error: error!)
} else {
callback(result: JSON(data : data!), response: response as? NSHTTPURLResponse, error: nil)
}
}
task.resume()
}
This does parse the data using SwiftJSON (see JSON(data : data!)), then when it comes to actually filling an array i use a class containing two attributes (one for the Main text in table and one for detail text)
class SomeClass {
let MainText : String
let DetailText : String
init(MainText : String, DetailText : String) {
self.MainText = MainText
self.DetailText = Detailtext
}
}
Now in the UITableView i have a .swift file and in the
override func ViewDidLoad() {
//code
}
I use a loop to get the data from the result callback in the GET method to append to an array of
var rows : [SomeClass] = []
This is very CPU intensive but I did not find another way to deal with this problem. I tried only displaying 50 rows in the table and only creating 50 class items for the rows. But none of that matters, what I fear is that the SwiftyJSON way of dealing with this problem is not the right one but i thought that maybe I am overlooking something.
If I understood your problem, you are worried about CPU / Energy Efficiency.
What you should consider, if it's not how your app already works, is implementing the parsing process in the background thread, make your [SomeClass] array observable and update the table when it changes (aka when the background parsing added an new value to it).
So first make your parsing function run in background (for instance with the Async GCD wrapper) :
func callback(JSON?, response: NSHTTPURLResponse, error: NSError?) {
Async.background {
//Do your JSON parsing stuff here, XXX is a SomeClass object
rows <- rows + [XXX]
}
}
You might have noticed the unusual syntax for the array appending method. That's because making your array "observable" is part of the solution. I advise you to get the Observable-Swift library to make it easier to observe.
Once added to your project, change your array declaration :
var rows = Observable([SomeClass]())
Now implement the method that will be called when your callback parsed a new item (for instance in your viewDidLoad:)
rows.afterChange += { self.table.reloadData() }
where table is your table view
If you want to implement a power-friendly runtime, you might want to update the table every time 50 or 100 objects are added to the array. This can be done so (if you want to do so do not implement the method right above):
rows.afterChange += { if $1.count / 100 = 1 { self.table.reloadData() }}
where 100 is the value of new object required to be added in order to update the table. With Observable-Swift, $0 represents the array before it was updated and $1 the array after its update.
One last thing : the rows array is no longer of type [SomeClass] but Observable<SomeClass>. If you want to access the [SomeClass] value, just replace rows by rows.value
Hope I didn't misunderstood your question. Anyway if I did, I think that can still help providing a better implementation of JSON parsing.
You should not be worried about how much of data you have to display in TableView.
TableView class handles everything for you as long as you pass the json object properly as a Tablesource.
It's actually a pretty good concern about how you use the resources. Normally, we will go with pagination if you don't want to query back whole amount of data from a request. Then, you will implement some proper logic based on the skip and limit in order to get further data.
As for the UITableView, there is nothing to worry about. Because, it's developed in an efficient way. The total number of cell in memory is the total number of cell visible. The UITableView will help populating the data via delegation methods. It's not like: you have 500 rows of data, then it has 500 UITableViewCell. It's reusability.
I've seen many tutorials and they really help me with understand parent-child managed object context and other things related to this. I am ready to start using it in my app but I have a question. Why nobody use singleton for keeping main managed object context. I guess it would be much better to extract Core Data related objects from AppDelegate and set it to own class right? Something like in this Tutorial at raywenderlich.com. But they still instantiate CoreDataStack class (no problem with this, singleton must be instantiate too) and when it's need they set managedObjectContext in prepareForSegue (and set it to first view controller from AppDelegate). Why not to remove this need and just use singleton CoreDataStack and have possible to use managedObjectContext in each controller if needed?
Second and bonus question: I think it's better to have less code in controller and more in other classes. I think it helps with readability. So what if I move this code from controller and set it for example to CoreDataStack class or some other class that helps with Core Data requests and responses:
func surfJournalFetchRequest() -> NSFetchRequest {
let fetchRequest =
NSFetchRequest(entityName: "JournalEntry")
fetchRequest.fetchBatchSize = 20
let sortDescriptor =
NSSortDescriptor(key: "date", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
I know it's possible but is it better? If you get app codes from me would it be better if in controller it would be one line CoreDataStack.fetchRequest("JournalEntry", sortedKey: "date")?
And what about if I take this code and insert it to singleton and created function with closure? I would created child managed context in singleton and do needed operations in there and in controller I would just changed UI:
func exportCSVFile() {
navigationItem.leftBarButtonItem = activityIndicatorBarButtonItem()
let privateContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateContext.persistentStoreCoordinator = coreDataStack.context.persistentStoreCoordinator
privateContext.performBlock { () -> Void in
var fetchRequestError:NSError? = nil
let results = privateContext.executeFetchRequest(self.surfJournalFetchRequest(), error: &fetchRequestError)
if results == nil {
println("ERROR: \(fetchRequestError)")
}
let exportFilePath = NSTemporaryDirectory() + "export.csv"
let exportFileURL = NSURL(fileURLWithPath: exportFilePath)!
NSFileManager.defaultManager().createFileAtPath(exportFilePath, contents: NSData(), attributes: nil)
var fileHandleError: NSError? = nil
let fileHandle = NSFileHandle(forWritingToURL: exportFileURL, error: &fileHandleError)
if let fileHandle = fileHandle {
for object in results! {
let journalEntry = object as! JournalEntry
fileHandle.seekToEndOfFile()
let csvData = journalEntry.csv().dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
fileHandle.writeData(csvData!)
}
fileHandle.closeFile()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationItem.leftBarButtonItem =
self.exportBarButtonItem()
println("Export Path: \(exportFilePath)")
self.showExportFinishedAlertView(exportFilePath)
})
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationItem.leftBarButtonItem = self.exportBarButtonItem()
println("ERROR: \(fileHandleError)")
})
}
}
}
I just want to be sure that my aproach would be okay and would be better than original. Thanks
I built my first core data app with a singleton pattern. It seemed logical for me because there is only one core data stack anyway. I was very wrong, the singleton pattern turned into a big mess quickly. I added more and more code to bend the singleton stack to something that works. In the end I gave up and I invested the time to replace the singleton mess with dependency injection.
Here are some of the problems I encountered before I dumped the singleton:
Since the app kept important data, my users requested a backup functionality. To restore from a backup I switched the sqlite file and then I would just create a new Core Data stack. Doing this in a clean way is next to impossible if you use a pull-pattern to get the managedObjectContext from a singleton. So my way to switch the Core Data stack was to tell the user that they have to restart the app. Followed by an exit(). Not the most elegant way to handle this.
After Apple added childContexts I decided to get rid of undo managers and context rollbacks, because that never worked 100% for me. But changing my editing viewControllers so they use child contexts which are discarded when the user hits cancel, was an incredible painful act because I now had a mix of singleton contexts and viewController local contexts in one viewController.
For editing the targets of relationships I had editViewControllers inside editViewController. Because I created the edit context inside the edit viewControllers I ended up saving data to the main context that shouldn't have been saved. It's a bit complicated to explain, but the second viewController saved stuff like new objects to the main context even if the user in the outer edit viewController hit cancel. Which always lead to orphaned objects. So I added more code to bend the singleton in a way that would make it less of a singleton.
I also had a CSV import function and I wanted to preview the data to the user before they press "Import". I build a totally new infrastructure for that. First I parsed the CSV into a data structure that basically duplicated my core data classes. Then I build a viewController to display these non core data classes, with even more code duplication. I would only start to create core data objects when the user pressed import.
After I got rid of the singleton pattern I could reuse the existing data display viewController. I would just give it a different context, in this case an in-memory context that contained the data that will be imported. Much cleaner, less duplicated code.
I guess some of these problems were not really the singletons fault. I was just very inexperienced.
But I still would strongly recommend against singleton core data.
would be one line CoreDataStack.fetchRequest("JournalEntry", sortedKey: "date")?
You don't need a singleton for this. Stuff like this should be in the NSManagedObject subclass you create for JournalEntry.
And what about if I take this code and insert it to singleton and created function with closure? I would created child managed context in singleton and do needed operations in there and in controller I would just changed UI:
And why don't you create a method that doesn't require internal state at all?
class func export(#context: NSManagedObjectContext, toCSVAtPath path: String,
progress: ((current: Int, totalCount: Int) -> Void)?,
completion: ((success: Bool, error: NSError?) -> Void)?) {
Much more flexible.