Objective-C: Avoiding EXCEPTIONS when NSConnection to Distributed Objects fail - objective-c

I'm using Objective-C Distributed Objects (DO) to share data from one application (that collects data from the network) to another (a patch inside Quartz Composer). When the connection to the distant object fails (when I shut down the first application), I'm get:
5/16/12 8:17:06.373 PM Quartz Composer: *** EXCEPTION IGNORED: connection went invalid while waiting for a reply because a mach port died
After that point, the Quartz composition is hung. Even after I bring the first application back up, it's still hung. I want the Quartz patch to reconnect.
I am using the Notification Center to put shut down the old objects, like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(connectionDidDie)
name:NSConnectionDidDieNotification
object:theConnection];
Right now, my counnectionDidDie looks like this:
- (void) connectionDidDie
{
NSLog(#"Connection died and we detected it");
[[self proxyObject] release];
[self setProxyObject:nil];
theConnection = nil;
}
I also check to be sure the connection is still alive, just before accessing any part of the proxyObject, like this:
if ([NSConnection defaultConnection]) { // this line triggers the exception
// access proxyObject
}
I've also tried
if ([theConnection isValid]) { // this line triggers the exception
// access proxyObject
}
In both cases, it's this test that triggers this EXCEPTION.
What can I do to prevent the hang of Quartz when I shutdown the first application, who has the vended object?

I was never able to find a way to shut down the DO connection fast enough to prevent QC, who draws as 30-60 frames per second, front testing the connection (and crashing) before connectionDidDie: was called. Ultimately, I've decided to use DO, only to get an initial copy of the object, which then do a deep copy of, and then kill the DO connection while nothing is trying to access it. DO just doesn't seem to be that great of solution, after you dig into it. :(

Related

Thread Handling with NSURLSessionDataTask

In a nutshell, I am trying to display data from a publicly available JSON file on the WEB. The process is the following:
I initiate the download with an NSURLSessionDataTask, then I parse and display the JSON or handle errors if they occur. Here is my relevant code:
- (void) initiateDownload {
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 5.0f;
sessionConfig.timeoutIntervalForResource = 20.0f;
NSURLSessionDataTask *downloadWeatherTask = [urlSession dataTaskWithRequest:urlRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *downloadError) {
if (downloadError) {
dispatch_sync(dispatch_get_main_queue(), ^{
[self errorReceived:downloadError];
});
} else if (data) {
dispatch_sync(dispatch_get_main_queue(), ^{
[self parseWeatherJSON:data];
});
}
}];
[downloadWeatherTask resume];
}
I have a couple of questions about this:
I am not all that familiar with thread handling. Although I added the
dispatch_sync(dispatch_get_main_queue(), ...)
to both completion blocks, and it seems to work, I am not sure this is the best way to be thread safe (before, I received all kinds of error messages and displaying the data took 10 seconds after the download has already finished). Is there a better way to handle the download and the threads or mine is an acceptable solution?
I would like the user to be able to initiate the download process manually any time he/she wants to refresh the data displayed. At first, I initialized the NSURLSessionDataTask once and made it available anywhere within the class; so I could just call resume every time a refresh is called. However, I could not find a command to re-do the download process. Once I called [downloadWeatherTask resume], I was unable to start the task from the beginning.
So, I added the task to a separate function (the one you see above) and initialize it there every time the function is called. This works fine, but I am not sure it is the best way to go. For example, is it memory safe or am I creating a new task every time the user initiates a refresh call and will eventually run out of memory?
Thank you for the answers!
A little more info: I use the latest XCode 11 and target iOS 9 and up.
NSURLSession will, by default, use a dedicated background serial queue for completion blocks (and delegate methods, should you do that). But you really want to make sure you trigger UI updates from the main queue (retrieved via dispatch_get_main_queue()). And you generally want to avoid updating properties and ivars from multiple threads (unless, they have some thread-safety built in to them, which is unusual), so dispatching the updates to those properties/ivars back to the main queue is a nice simple way to achieve thread safety.
So, bottom line, what you’re doing here is fine.
However, I could not find a command to re-do the download process.
You perform (resume) a given task only once. If you want to perform a similar request again, instantiate a new NSURLSessionDataTask.

Using AFIncrementalStore with a WebSockets Application

I'm attempting to adopt AFIncrementalStore for a Mac app that talks to App.Net. Unlike the example applications that come with the framework, I'm using the streaming APIs, with a websocket connection. For this I was using SocketRocket. These parts are working fine: I'm able to set up a request connection to ADN and get a connection ID back. It's this connection ID I supply to the later requests to ADN APIs.
My problem is that the Core Data stack is initialized and firing before I get my first connection ID back from ADN. I'm not sure how to handle this situation.
Currently, I have this code in my app delegate:
self.socketConnection = [[MUNConnectionManager alloc] init];
self.socketConnection.delegate = self;
My connection manager implements a delegate that calls back to the app delegate when the connection ID has been received:
# pragma mark MUNConnectionManager delegate method
- (void)didReceiveConnectionId:(NSString*)connectionId
{
self.connectionId = connectionId;
}
So once this connection ID is received, that's when I'd like to boot AFIncrementalStore into action. But this is perhaps a full second or so after launch, and my AFIncrementalStore client is already crapping out because it doesn't have that connection ID.
Any suggestions appreciated!
I think I may have found the answer to this. In my XIB I have an array controller with the "prepares content" checkbox on. That would have triggered the data store and loaded up all the Core Data stack. When I uncheck that box it doesn't load, and my ADN delegate is free to pull the ID.
So if anyone else runs into this, the answer is the CD stack doesn't load until you try to hit it.

What can cause a CFSocket to close itself?

I have a class method which requires an underlying CFSocket, and this method gets called very often so it's too expensive to create and destroy a new socket each time the method runs. So instead I've created a static instance of a CFSocketRef so I can share a single socket between method calls:
+(void)myStaticMethod {
static CFSocketRef udpSocket;
#synchronized(self) {
if (!udpSocket) {
udpSocket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, NULL, NULL);
int yes = 1;
setsockopt(CFSocketGetNative(udpSocket), SOL_SOCKET, SO_NOSIGPIPE, (void *)&yes, sizeof(yes));
}
}
//method body
}
I have two questions:
Do I have to worry about destroying (invalidating) the socket when the app will terminate, or does it close itself?
Which events might cause the socket to close itself and how would you prevent potentially writing to a bad CFSocketRef?
If an app really terminates then all resources, including sockets, are released. But normally apps don't terminate but go into the background and in that case the socket can become invalid. This is described quite well in Technical Note TN2277. See in particular the section "Data Socket".
Since the creation of an UDP socket is a simple operation, the following advice from TN2277 would apply to your code:
If reopening your data socket is simple, cheap, and has no user
visible consequences, the best approach is to close the socket when
the app goes into the background, and reopen it when it comes back
into the foreground. This makes your life very simple; in fact, if you
do this, you can ignore the rest of this section entirely!

In iOS does either NSURL or NSXML span a new thread?

I have a program that progresses as follows. I call a method called getCharacteristics. This method connects to a remote server via a NSURL connection (all networking code done in another file) and when it receives a response it makes a method call back to the original class. This original class then parses the data (xml) and stores its contents as a map.
The problem I'm having is that it appears that somewhere in this transaction another thread is being spawned off.
Here is sample code showing what I'm doing:
#property map
- (void) aMethod
{
[[WebService getSingleton] callWebService: andReportBackTo: self]
Print "Ready to Return"
return map;
}
- (void) methodThatIsReportedBackToAfterWebServiceRecievesResponse
{
//Parse data and store in map
Print "Done Parsing"
}
The problem that I am running into is that map is being returned before it can be fully created. Additionally, "Ready to Return" is being printed before "Done parsing" which suggests to me that there are multiple threads at work. Am I right? If so, would a simple lock be the best way to make it work?
NSURLConnection will execute in another thread if you tell it to execute asynchronously.
In my opinion the best way to deal with this would be to write your own delegate protocol, and use delegation to return your map when the you have downloaded and parsed your data.
You could retrieve your data synchronously using NSURLConnection, but you may force the user to wait for an extended period of time especially if a connection timeout occurs. I would avoid this approach.

'No database channel is available'

I have an app which connects to the internet and stores data in an SQL database. I tested with iOS4, it works completely as it should. When I upgrade to the new version though, I get an NSInternalInconsistencyException, with this as the reason:
'_obtainOpenChannel -- NSSQLCore 0x951a640: no database channel is available'
From what I can gather, my database is being accessed by something when it shouldn't be, though I can't understand where or why.
Can anyone help me locate and properly diagnose my problem?
I found something for this one:
I got the error (among some other ones, seemingly randomly appearing) while I was accessing a managed object's relationships in a different thread than the one the managed context was created in. There have been some changes with respect to concurrent access to managed objects in iOS5 (see here http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-CoreData/_index.html#//apple_ref/doc/uid/TP40010637) - and although the doc states the default behaviour should be as pre-iOS5 it apparently is not true, my code did work without problems in iOS4.2.
For now, my workaround was to do all the relationship-access thingies in the main thread, store the data in an array, and access the data I need in the other thread via that array. No more errors at least. This is not the 'nice' solution I suppose, as I should (and will) change the way I concurrently access managed objects, but I'm not going to change that in a hurry now.
This default concurrency type for NSManagedObjectContext is NSConfinementConcurrencyType, which means it can only be used by a single thread. From the documentation:
You promise that context will not be used by any thread other than the
one on which you created it.
You can instead create a managed object context that is backed by a private queue for multithreaded use:
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]
To use the managed object context from a different thread, use performBlock: (asyncronous) or performBlockAndWait: (synchronous), e.g.
__block NSArray *results;
[[self managedObjectContext] performBlockAndWait:^{
results = [[self managedObjectContext] executeFetchRequest:request error:&error];
}];
// do something with results
The documentation says you don't need to use the block API from the thread that created the managed object context.
Another option is to create separate managed object contexts for each thread.
See the iOS 5 release notes for more info.