i am currently developing an application for iOS which needs to communicate with a hardware device using a Socket Connection.
Therefore i am using a Singleton Object with NSStream. To this point all works as expected.
The problem is if the connection is terminated, or interrupted it is not possible to reopen it (this is stated in the Documentation).
So my idea is to destroy the Singleton and recreate it. This should not interfere with the Singleton Pattern, because it states that there only exists one copy of such a class.
Has anyone an idea how to solve this problem?
Any other solution not involving the recreation of the singleton would be highly appreciated.
Why not put some logic in your singleton class to test if the connection to the device is active. If it has died, close the connection, and open a new one. This is effectively the same thing you are trying to do by destroying a recreating the singleton, but doesn't abuse the singleton pattern quite as much. It should also be simpler, because only the singleton knows about the connection, and thus keeps coupling low.
Singleton are not made to be destroyed, probably you should include a method to re-open the singleton class and close/reset old connections.
The heart of Singleton that lets you create a variable once, and only, this means it will live along your app till it is terminated. We init it as Lazy loading, means when it is used, then initial it. It will allocate in RAM as static, and thus we can call it to reuse anytime, this is really save time.
So MUST NOT destroy Singleton, just do some logic inside it.
Hope this help.
Related
Is there any point to using retain on a singleton? I believe the whole point of using the singleton pattern is to keep one global object to access from various classes. What would be a case to use retain on such an object?
Generally, the implementation of retain in a singleton class returns self (not singleton instance) like below:
-(id)retain
{
return self;
}
I recently went through some open source code, and the author repeatedly retained the singleton
object = [[SingletonClass shareObject] retain]
and released it in dealloc.
So when I tried to build this project, it worked at first, then crashed when it attempted to access variables on singleton object later.
What would happen exactly, if I retain a singleton object and attempt to access it?
Just because it's a singleton doesn't mean its lifetime is necessarily the lifetime of the process. For example, you might have some singleton that obtains a resource. But until you need that resource, there's no point in creating it. Likewise, you might know that all instances are done with that resource, so why keep it around? An easy way to keep track of whether a singleton needs to continue to exist is its retain count.
For example, you might have a singleton that represents the video camera built-in to a user's device. There might be different parts of your application that need to access the camera. But it might also be possible to use your app without the camera, or the workflow may dictate that the camera is turned off at some point (for privacy, or whatever). So you don't allocate the singleton until the user initiates an action that starts camera use. They may do several actions with the camera, such as take a picture, scan a QR code, record some video, etc. Then they may end all of those actions and need the memory taken by the camera freed up (especially on a mobile device). So they end those actions. If each camera-related actions retained the camera singleton, then each released it when they were done, you could free up the memory and other resources used by the camera, even though it's still a singleton.
Consistency. Make a point of always retaining objects that you keep references to, even if they're singletons, and you won't make mistakes down the road when you forget to retain objects that reference counts do matter for.
Never hold on to a singleton. There is no guarantee that the underlying library/class/manager doesn't replace that singleton with a new object in the application's lifetime. The point of a singleton access static method is for the current valid singleton to be available at any time to any caller.
Singletons are written by anyone and there is no guarantee that the singleton is not switched. The only guarantee is that a specific class will only have one existing instance at any time.
If you retain a singleton at some point the object you retained could be invalid or even worse, be completely different from the one being held to by the class singleton() method.
I'm creating my managed object context with NSPrivateQueueConcurrencyType concurrency type.
Also I'm using performBlock: selector to execute operations in background. So If I'm fetching some objects in background (in performBlock:), is it safe to use resulting managed objects in main thread?
As a general rule, no it is not safe to share NSManagedObject instances across threads no matter what concurrency type you are using.
However there is a library you can use to make your context(s) and object instances thread-safe. With that you can pretty much ignore all the nonsense about ensuring thread isolation between contexts and focus your efforts on the things that matter, like building out the actual functionality of your app.
I'm not 100% sure, but in my own experience I do it this way: If you are changing the variables properties, do it inside performBlock. I had one case where reading was causing some weird behavior, but in general it seems to be OK. If you want to be extra safe, use performBlock every time you touch a managed object in any way.
You will need to use a different context for each thread as explained here iOS Developer - Core data multithreading
One way to implement is described at Core Data - one context per thread implementation
Sorry, I should've search better, here is exactly my question & answer to it:
Core Data's NSPrivateQueueConcurrencyType and sharing objects between threads
Here's my scenario: I have a thread running heavy calculations, saving the results via core data at the end. I have the thread set up with it's own autorelease pool and it's own NSManagedContext created within the thread. The user can change the inputs to the calculation on the main thread, in which case the calculation thread is terminated (via regular checks of a NSLocked variable) and relaunched with the new inputs. Everything is working fine here.
For performance reasons, the calculation thread doesn't have an undo manager and there is only one context save at the very end. If a termination command is detected I don't want to save the results. Right now I'm just skipping the context save and releasing it, which seems to work fine.
I noticed, however, that there's a reset method for NSManagedContext. Apple's documentation on this method isn't very helpful to me. It simply states that it returns the receiver's contents to it's base state and that all the receiver's managed objects are "forgotten".
What does that mean? Is it the equivalent to reverting to the last saved version? Is an undo manager required for proper operation of this method? Any reason I should use this method instead of what I'm doing now?
It sounds like you are using the context to cache changes independent of the context on the main thread, and if you don't want those changes to be recorded, you just throw them out by deleting the "local" context. This is good enough for the scenario you are describing. -reset might be useful if you didn't want to relaunch the background thread, but just start over using the same thread (and context), but with new inputs. Since you launch a new thread (thus creating a new NSManagedObjectContext on it), -reset is probably not very useful for you in this scenario. You already pretty much doing it as Apple recommends in several of their sample codes.
What technical reasons are there not to make a singleton class to manage my Core Data? I'm trying to make a decision now, if I should strip out all of the boilerplate core data code and re-implement it in a singleton.
The boilerplate code in the application delegate in the Xcode templates is functionally implemented as a singleton. The application object is a singleton and it maintains but one delegate object so you've only got one instances of the Core Data stack and since the application object is universally accessible, you can always get to the app delegate as well.
However, even that works only for simple apps with one persistent store with all the context using that one store. In more complex apps you may have multiple stores or context so a singleton quickly becomes too bloated.
A singleton usually won't buy you much complexity hiding or save duplicate coding because most of the coding you have to do with Core Data is in the controller layer where you link up the model to the view/interface. Since that logic is usually custom to each view, you can't actually park it in the singleton.
I've used singletons in the past but in the end they usually prove more hassle than they are worth.
There are two important considerations (note these are not the only two) when deciding if a singleton is right for you:
Threading
Memory Usage
Threading
Singletons are convenient, but if your application uses multiple threads you might be tempted to write something like this:
[[CDSingleton managedObjectContext] executeFetchRequest:someFetch];
//later on a background thread you might write
NSManagedObject *object = [[CDSingleton managedObjectContext] objectWithID:objectID];
Shortly after that, your application will crash because you've accessed a managedObjectContext which was likely created on the main thread from some other thread.
Memory Usage
Singletons never go away, that's the point of a Singleton. Thus, they also never willingly free their consumed resources. In the case of CoreData that means the managed object context will continue to hold managed objects in memory until you call -reset or -save:.
That could be bad if your app uses a lot of data.
Best practice is to pass the managed object context between view controllers. Apple documentation and samples do that. You should never really have to access your app delegate, not for Core Data, not for anything.
http://www.cimgf.com/2011/01/07/passing-around-a-nsmanagedobjectcontext-on-the-iphone/
I'm working on some code that uses an NSOperation to import data. I'd like for the user to be able to undo the NSManagedObject instances that are created during the import operation.
From what I can tell, it's impossible to use the NSManagedObjectContext -undoManager for any operations that are performed off of the main thread. From the Core Data Programming Guide section on Use Thread Confinement to Support Concurrency, we have these two conditions:
Only objectID should be passed
between managed object contexts (on
separate threads)
Managed objects
must be saved in a context before
the objectID can be used.
This makes sense since the managed objects need to be moved from private storage (NSManagedObjectContext) to public storage (NSPersistentStore) before they can be shared.
Unfortunately, the -save: message also causes any managed objects in the undo stack to be removed. From the Memory Management Using Core Data section of the same guide:
Managed objects that have pending
changes (insertions, deletions, or
updates) are retained by their context
until their context is sent a save:,
reset , rollback, or dealloc message,
or the appropriate number of undos to
undo the change.
I've tried several things to work around this limitation, and everything eventually leads back to bulk of the work happening on the main thread (and spinning beach balls.) Any clues to getting undo working with objects created off the main thread would be very much appreciated.
--
An enhancement Radar has been submitted: rdar://problem/8977725
This answer will probably be a bit of a back and forth. If I understand the issue correctly, you are doing an import but when the import is done you want the user to be able to select what gets saved from the import?
If that is not correct, please fix my assumptions and I will update this answer.
If it is correct then what you can do is:
Change your background object creation to
NSEntityDescription *myEntity = ... //Entity from your context
[[NSManagedObject alloc] initWithEntity:myEntity
insertIntoManagedObjectContext:nil];
Store these entities in an array.
Pass the entities back to your main thread as needed.
Release on any objects you don't want to keep
Call [myMainContext insertObject:managedObject] on any you want to keep.
Perform a save on the NSManagedObjectContext.
Since these entities are not part of a NSManagedObjectContext yet they only exist in memory and should be thread safe since they are not yet tied down to a NSManagedObjectContext.
This is of course theoretical and will require testing. However it should accomplish your goal.
Not an expert, but I think what you're going to need to do is create a second context to perform the operations, then merge the two contexts together. You should be able to manage the merge as an undo step. Note, this only works if you're treating the entire set of operations as one undo step, as far as the user is concerned.
Suppose that you use a separate context for the background thread, and once it's done, push a [[backgroundContext undoManager] undo] onto the foreground thread's undo stack? I've never tried anything like that, but off the top of my head I can't think of a reason it shouldn't work.
One option may be to make your import thread persistent. Even when the thread is finished importing, it goes into an idle loop state. This way your threaded ManagedObjectContext is persisted in the proper thread. Then when the user wishes to undo a change, send a message to the thread to use the undomanager.
It's incredibly likely you've considered this and you're likely only looking for a solution using the existing undoManager, but just in case:
Since you're inserting objects and not updating existing ones, you do have the power to tag them with a transaction id as each batch is imported, deleting them in a background thread in the case of an undo. A simple incremented NSNumber is sufficient for the tag.
Inelegant, but workable.