How Do I Do Distributed Objects on OSX with Objective C? - objective-c

As of 2016, the Apple docs on this are stale and don't work. For instance, they mention "retain", but in XCode 7.1 the default is to use ARC and it doesn't support "retain". I tried various examples on the web and none worked. How do I code the IPC mechanism called Distributed Objects on OSX, where a client application can call class methods on a server application (like one especially composed in a LaunchDaemon, but not required)?

Here's a code sample to get you going. The server.mm project is probably best that you load it into a LaunchDaemon. I ran some tests with the daemon running as root user, and sure enough the client application, which was running as "mike", ran the code in the daemon as "root". So, it enables privilege elevation. Note that this IPC doesn't provide any protocol encryption or authentication challenges -- so, it's up to you to add that yourself. You can probably get away with a key/list, XML, or JSON message that's encrypted with AES256 + Base64 encoding with a long, tough password phrase both on sending and receiving. Remember, with privilege elevation, it's very important that you put some protection mechanisms in place.
Launch the server first and it will sit there, waiting on connections. Launch the client next and it will establish a connection, pass data to a sample class method, wait and receive a message back, and then display it and shut down. The server will also show the connection was made and what was received on the server before a response back was sent.
Note that this is a synchronous example, meaning you call the class method and it waits for a response. If you want it to be asynchronous, then you should read the Apple documentation on the oneway keyword. You put it in both the client and server in the class method declaration. Just note that the oneway keyword is really only best used with a class method that returns void because you can't get a response back on an asynchronous class method. So, you'd do an async call to start a task, and then use a synchronous call to get a status update on that task you started. So, here's an example of a class method declaration that would have the oneway keyword added:
- (oneway void)runTaskAsync:(NSString *)sParam;
And now, the code...
server.m
#import <Foundation/Foundation.h>
#define cat stringByAppendingString
#interface MyService : NSObject {
NSConnection *connection;
}
#end
#implementation MyService
- (NSString *)testResponse:(NSString *)s {
NSLog(#"...connection:%#", s);
s = [s cat:#"-response"];
return s;
}
- (void)runService {
connection = [[NSConnection alloc] init];
[connection setRootObject:self];
[connection registerName:#"com.acme.myservice"];
[[NSRunLoop currentRunLoop] run];
}
#end
int main (int argc, const char *argv[]) {
#autoreleasepool {
NSLog(#"ACME MyService 1.0\n");
MyService *svc = [[MyService alloc] init];
[svc runService];
}
return 0;
}
client.m
#import <Foundation/Foundation.h>
int main (int argc, const char *argv[]) {
#autoreleasepool {
NSLog(#"building proxy object");
id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:#"com.acme.myservice" host:nil];
NSLog(#"calling test response thru proxy object");
NSString *sResult = [proxy testResponse:#"sent"];
NSLog(#"RESULT=%#", sResult);
}
return 0;
}

Related

How to debug communication between XPC service and client app in OSX

I'm trying to write a simple pair of "client app" & "XPC service". I was able to launch xpc service from client (i.e I can see service running in the Activity monitor processes list), but when I try to send any request, that has a response block, I get an error: "Couldn’t communicate with a helper application."
The worst thing here is that error doesn't give me any info about what went wrong. And I'm also unable to debug the service properly. As I understand, the correct way to do this is to attach a debugger to process (Debug->Attach to process, also see here). I have both client and service projects in a single workspace.
When I run client from xcode and try to attach debugger to launched service, that ends with a "Could not attach to pid : X" error.
If I archive the client app run it from app file and then try to attach debugger to service the result is the same.
The only way to record something from the service I could imagine is to write a logger class, that would write data to some file. Haven't tried this approach yet, however that looks insane to me.
So my question is:
a) How to find out what went wrong, when receiving such non-informative response like: "Couldn’t communicate with a helper application"?
b) And also, what's the correct way to debug the xpc service in the first place? The link above is 5 years old from now, however I can see that some people were saying that "attach to debugger" wasn't working.
The code itself is fairly simple:
XPC service, listener implementation:
#import "ProcessorListener.h"
#implementation ProcessorListener
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
[newConnection setExportedInterface: [NSXPCInterface interfaceWithProtocol:#protocol(TestServiceProtocol)]];
[newConnection setExportedObject: self];
self.xpcConnection = newConnection;
newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: #protocol(Progress)];
// connections start suspended by default, so resume and start receiving them
[newConnection resume];
return YES;
}
- (void) sendMessageWithResponse:(NSString *)receivedString reply:(void (^)(NSString *))reply
{
reply = #"This is a response";
}
- (void) sendMessageWithNoResponse:(NSString *)mString
{
// no response here, dummy method
NSLog(#"%#", mString);
}
And the main file for service:
#import <Foundation/Foundation.h>
#import "TestService.h"
#interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
#end
#implementation ServiceDelegate
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
// This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection.
// Configure the connection.
// First, set the interface that the exported object implements.
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(TestServiceProtocol)];
// Next, set the object that the connection exports. All messages sent on the connection to this service will be sent to the exported object to handle. The connection retains the exported object.
TestService *exportedObject = [TestService new];
newConnection.exportedObject = exportedObject;
// Resuming the connection allows the system to deliver more incoming messages.
[newConnection resume];
// Returning YES from this method tells the system that you have accepted this connection. If you want to reject the connection for some reason, call -invalidate on the connection and return NO.
return YES;
}
#end
int main(int argc, const char *argv[])
{
// [NSThread sleepForTimeInterval:10.0];
// Create the delegate for the service.
ServiceDelegate *delegate = [ServiceDelegate new];
// Set up the one NSXPCListener for this service. It will handle all incoming connections.
NSXPCListener *listener = [NSXPCListener serviceListener];
listener.delegate = delegate;
// Resuming the serviceListener starts this service. This method does not return.
[listener resume];
return 0;
}
For client app, the UI contains a bunch of buttons:
- (IBAction)buttonSendMessageTap:(id)sender {
if ([daemonController running])
{
[self executeRemoteProcessWithName:#"NoResponse"];
}
else
{
[[self.labelMessageResult cell] setTitle: #"Error"];
}
}
- (IBAction)buttonSendMessage2:(id)sender {
if ([daemonController running])
{
[self executeRemoteProcessWithName:#"WithResponse"];
}
else
{
[[self.labelMessageResult cell] setTitle: #"Error"];
}
}
- (void) executeRemoteProcessWithName: (NSString*) processName
{
// Create connection
NSXPCInterface * myCookieInterface = [NSXPCInterface interfaceWithProtocol: #protocol(Processor)];
NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: #"bunldeID"]; // there's a correct bundle id there, really
[connection setRemoteObjectInterface: myCookieInterface];
connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(Progress)];
connection.exportedObject = self;
[connection resume];
// NOTE that this error handling code is not called, when debugging client, i.e connection seems to be established
id<Processor> theProcessor = [connection remoteObjectProxyWithErrorHandler:^(NSError *err)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle: #"OK"];
[alert setMessageText: err.localizedDescription];
[alert setAlertStyle: NSAlertStyleWarning];
[alert performSelectorOnMainThread: #selector(runModal) withObject: nil waitUntilDone: YES];
}];
if ([processName containsString:#"NoResponse"])
{
[theProcessor sendMessageWithNoResponse:#"message"];
}
else if ([processName containsString:#"WithResponse"])
{
[theProcessor sendMessageWithResponse:#"message" reply:^(NSString* replyString)
{
[[self.labelMessageResult cell] setTitle: replyString];
}];
}
}
Jonathan Levin's XPoCe tool is helpful when you can't attach a debugger.
You can add logging NSLog() or fprintf(stderr,...) to your service and clients, specifically around the status codes. You just have to specify the path of the file to write stdout and stderr. <key>StandardErrorPath</key> <string>/tmp/mystderr.log</string>
There's a section on Debugging Daemons at this article on objc.io .

Handle any application closing in objective c

I want to execute my method when any application is closing. My code is:
#interface FO: NSObject
- (void)applicationKilled:(NSNotification*)notification;
- (void)appDidLaunch:(NSNotification*)notification;
#end
#implementation FO
- (void)applicationKilled:(NSNotification*)notification {
NSLog(#"success");
}
- (void)appDidLaunch:(NSNotification*)notification {
NSLog(#"app info: %#", [notification userInfo]);
}
#end
#implementation Main:NSObject
FO fo;
NSString * filePath = "...MyPath";
NSString * application = "..MyApplication";
int main(int argc, const char * argv[]) {
fo = [[FO alloc]init];
[Main MyMethod];
while(1==1) {
...some code;
}
return 0;
}
+(void) MyMethod {
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver:fo selector:#selector(appDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
[center addObserver:fo selector:#selector(applicationKilled:) name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
[[NSWorkspace sharedWorkspace] openFile:filePath withApplication:application]; }
#end
However, appDidLaunch method is not firing, even if i'll open another application in finder. Also applicationKilled method is never firing.
When i'm executing following code
[center postNotificationName:NSWorkspaceDidLaunchApplicationNotification
object:self];
appDidLaunch method is firing OK. Where can be a problem? Should this methods be fired every time when some application is opened or closed?
CRD is on the right track. You absolutely must have a runloop to receive this notification. For example:
#implementation Main : NSObject
- (void)applicationDidFinishLaunching:(NSApplication *)app {
[Main MyMethod];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... The rest of your program ...
});
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyDelegate *delegate = [Main new];
[[NSApplication sharedApplication] setDelegate:delegate];
[NSApp run];
}
return 0;
}
I've put "the rest of your program" into a dispatch_async because you must not block the main thread. The usual way that Mac apps work is not with a big while (YES) loop. The usual way is by registering for various events and then waiting from them to happen. That's what the run loop is for. But if you have to manage your own loop (you generally shouldn't, but if you must), then you need to move it off of the main queue.
Assuming you are using ARC and also guessing as the information you give seems to be incomplete:
In your updated question you show fo declared as a local variable of MyMethod. The method addObserver:selector:name:object: does not keep a strong reference to the observer. After MyMethod returns the local fo object will be reclaimed, you now have no observer to call methods on.
However, while the above would explain why your code doesn't work it wouldn't explain why your app does not crash - and you don't report that it crashes. Running the code you give above causes the app to crash. So it appears that you've missed some information out or at least not reported the crash.
Guess Two
You have no run loop.
Many parts of the framework rely on there being a run loop which dispatches incoming events to appropriate handlers - just type "run loop" into Xcode's help. If you create a standard application using Xcode's "Cocoa Application" template the run loop is created for you by the code in main.m.
Events produced by OS X when applications start and stop are dispatched by the run loop to framework handlers which produce the corresponding notifications. Without a run loop these system events will not be handled, so no notifications.
You have:
int main(int argc, const char * argv[])
{
fo = [[FO alloc]init];
[Main MyMethod];
while(1==1)
{
...some code;
}
return 0;
}
so unless "...some code" creates a run loop the system events will not be handled.
Write your project using the standard "Cocoa Application" template and, for example, put your call to setup the notification handlers in applicationDidFinishLaunching:.
HTH

How can I signal one thread to wait until a download is finished and after that pass the data to waiting thread

I have a singleton class to download some data from the web. I am calling the download method of the singleton class from other class 'A' inside a GCD Queue and the downloads starts. The same time I am also executing this download method from a class 'B' in a GCD Queue. In this situation I want to inform the Class 'B' to wait until the download is completed. And when the download is completed give a copy of the downloaded data to class 'B' also. Here I am trying to download the same file from two classes A and B, otherwise there is no problem in my implementation. How is it possible?
Means here I am calling the same method in different threads. So how can I signal the thread B that the same file download is in progress in thread A and when finished pass the data to thread B also
- (NSData *)Download:(NSString *)urlString{
// here I am doing all the downloading operations
return data;
}
Downloader.h
// Signature of a block that is called with the downloaded data from a URL
// when the download is complete
typedef (void)(^)(NSData *) DownloadCompletedBlock;
// Public interface of a class that downloads data from URLs
// Downloads take place on a private dispatch queue, which
// downloads URLs one at a time
// Previously downloaded URLs are cached in a dictionary
// Every so often the cache should be processed to discard old
// entries. This will stop the cache from growing too large.
// Since all downloads happen on the same dispatch queue,
// accesses to the cache are naturally serialized without the need
// for a lock
#interface Downloader : NSObject
// Download the contents of a URL
// When the download is complete downloadCompleted will
// be executed on the callbackQueue to pass the downloaded
// data as a result
// This is the method that thread A should call
- (void)download:(NSString *)URLString
calbackQueue:(dispatch_queue_t)callbackQueue
completionBlock:(DownloadCompletedBlock)downloadCompleted;
// Download the contents of a URL blocking the thread that calls the
// method
- (NSData *)downloadBlocking:(NSString *)URLString;
#end
Downloader.m
// Private implementation interface
#interface Downloader ()
// The thread/queue on which all downloads take place
#property (readwrite, atomic) dispatch_queue_t downloadQueue;
// A cache of previously downloaded URLs
#property (readwrite, atomic) NSMutableDictionary *cachedDownloads;
// Download the contents of a URL and cache them
- (NSData *)downloadAndCacheUrl:(NSString *)URLString;
#end
// Implementation
#implementation Downloader
// Create the download queue and cache
- (id)init {
self = [super init];
if (self) {
downloadQueue = dispatch_queue_create("downloadQueue", NULL);
self.cachedDownloads = [NSMutableDictionary dictionary];
}
return self;
}
// Download the URL aynchronously on the download queue.
// When the download completes pass the result to the callback queue
// by calling downloadCompleted on the callback queue
- (void)download:(NSString *)URLString
calbackQueue:(dispatch_queue_t)callbackQueue
completionBlock:(DownloadCompletedBlock)downloadCompleted {
dispatch_async(self.downloadQueue, ^{
NSData *downloadedData = [self downloadAndCacheUrl:URLString];
dispatch_async(callbackQueue, ^{
downloadCompleted(downloadedData);
});
});
}
// Download the data blocking the calling thread
// Use a block variable to store the result
// Since the downloaded data is immutable, we do not need
// to worry about synchronizing it or copying it
// Use dispatch_sync to wait until the result is available
// If the data is already in the cache because thread A downloaded it
// then the cached data is used.
// Since downloads happen serially, there is only ever one download happening
// at a time so the download will only happen once
- (NSData *)downloadBlocking:(NSString *)URLString {
__block NSData *downloadedData = nil;
dispatch_sync(self.downloadQueue, ^{
downloadedData = [self downloadAndCacheUrl:URLString];
});
return downloadedData;
}
// Download the content of a URL. If the data has already been
// downloaded and cached, then use the cached value
- (NSData *)downloadAndCacheUrl:(NSString *)URLString {
NSURL *URL = [NSURL URLWithString:*)URLString];
NSData *downloadedData = [self.cachedDownloads objectForKey:URL];
if (downloadedData) {
return downloadedData;
}
downloadedData = [NSData dataWithContentsOfURL:URL];
if (downloadedData) {
[self.cachedDownloads setObject:downloadedData forKey:URL];
}
}
#end
Hope this is clear enough

What's the correct way to stop a background process on Mac OS X?

I have an application with 2 components: a desktop application that users interact with, and a background process that can be enabled from the desktop application. Once the background process is enabled, it will run as a user launch agent independently of the desktop app.
However, what I'm wondering is what to do when the user disables the background process. At this point I want to stop the background process but I'm not sure what the best approach is. The 3 options that I see are:
Use the 'kill' command.
Direct, but not reliable and just seems somewhat "wrong".
Use an NSMachPort to send an exit request from the desktop app to the background process.
This is the best approach I've thought of but I've run into an implementation problem (I'll be posting this in a separate query) and I'd like to be sure that the approach is right before going much further.
Something else???
Thank you in advance for any help/insight that you can offer.
The daemon could handle quit apple events or listen on a CFMessagePort.
If you use signals you should handle the signal, probably SIG_QUIT, that is sent instead of just letting the system kill your process.
If you have cleanup that may take a while, use something other than signals. If you are basically just calling exit, then signals are fine.
If you already have a CFRunLoop going then use CFMessagePort. If you are already handling apple events than handle quit.
CFMessagePort is a wrapper around CFMachPort that provides a name and some other conveniences. You can also use the NS wrappers for either.
I found an easier way to do this using an NSConnection object. I created a very simple ExitListener object with this header:
#interface ExitListener : NSObject {
BOOL _exitRequested;
NSConnection *_connection;
}
#property (nonatomic, assign) BOOL exitRequested;
- (void)requestExit;
#end
and this implementation:
#implementation ExitListener
#synthesize exitRequested = _exitRequested;
// On init we set ourselves up to listen for an exit
- (id)init {
if ((self = [super init]) != nil) {
_connection = [[NSConnection alloc] init];
[_connection setRootObject:self];
[_connection registerName:#"com.blahblah.exitport"];
}
return self;
}
- (void)dealloc {
[_connection release];
[super dealloc];
}
- (void)requestExit {
[self setExitRequested:YES];
}
#end
To setup the listener, the background process simply allocates and inits an instance of the ExitListener. The desktop application then asks the background process to exit by making this call:
- (void)stopBackgroundProcess {
// Get a connection to the background process and ask it to exit
NSConnection *connection = [NSConnection connectionWithRegisteredName:#"com.blahblah.exitport" host:nil];
NSProxy *proxy = [connection rootProxy];
if ([proxy respondsToSelector:#selector(requestExit)]) {
[proxy performSelector:#selector(requestExit)];
}
}
Using NSMachPorts directly seemed to lead to far more problems in registering and obtaining references. I found that NSConnection is the simplest way to create a basic communication channel for the sort of situation that I needed to solve.

Cocoa: Checks required for multiple asynchronous NSURLConnections using same delegate functions?

This is with reference to the StackOverflow question Managing multiple asynchronous NSURLConnection connections
I have multiple asynchronous HTTP requests being made at the same time. All these use the same NSURLConnection delegate functions. (The receivedData object is different for each connection as specified in the other question above. In the delegate, I parse the receivedDate object, and do additional operations on those parsed strings)
Everything works fine for me so far, but I'm not sure if I need to do anything to ensure correct “multithreaded” behavior.
Is it possible that more than two connections will use the delegate at the same time? (I would think yes)
If yes, how is it resolved? (Does Cocoa do this automatically?)
Do I need to have additional checks in place to ensure that each request is handled “correctly”?
I enhanced the Three20 library to implement asynchronous connections across multiple threads in order to fetch data even if the user was playing with the UI. After many hours of chasing down random memory leaks that were detected within the CFNetwork framework I finally root caused the issue. I was occasionally losing track of responses and data.
Any data structures which are accessed by multiple threads must be protected by an appropriate lock. If you are not using locks to access shared data structures in a mutually exclusive manner then you are not thread safe. See the "Using Locks" section of Apple's Threading Programming Guide.
The best solution is to subclass NSURLConnection and add instance variables to store its associated response and response data. In each connection delegate method you then cast the NSURLConnection to your subclass and access those instance variables. This is guaranteed to be mutually exclusive because every connection will be bundled with its own response and data. I highly recommend trying this since it is the cleanest solution. Here's the code from my implementation:
#interface TTURLConnection : NSURLConnection {
NSHTTPURLResponse* _response;
NSMutableData* _responseData;
}
#property(nonatomic,retain) NSHTTPURLResponse* response;
#property(nonatomic,retain) NSMutableData* responseData;
#end
#implementation TTURLConnection
#synthesize response = _response, responseData = _responseData;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate {
NSAssert(self != nil, #"self is nil!");
// Initialize the ivars before initializing with the request
// because the connection is asynchronous and may start
// calling the delegates before we even return from this
// function.
self.response = nil;
self.responseData = nil;
self = [super initWithRequest:request delegate:delegate];
return self;
}
- (void)dealloc {
[self.response release];
[self.responseData release];
[super dealloc];
}
#end
/////////////////////////////////////////////////////////////////
////// NSURLConnectionDelegate
- (void)connection:(NSURLConnection*)connection
didReceiveResponse:(NSHTTPURLResponse*)response {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
ttConnection.response = response;
ttConnection.responseData = [NSMutableData
dataWithCapacity:contentLength];
}
- (void)connection:(NSURLConnection*)connection
didReceiveData:(NSData*)data {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
[ttConnection.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
if (ttConnection.response.statusCode == 200) {
// Connection success
}
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
// Handle the error
}
Assuming you're launching all of the (asynchronous) connections on a single thread, then the delegate messages will all get posted in that thread's run loop. Therefore the delegate only needs to be able to deal with one message being handled at once; the run loop will hand one message off at a time. This means that while the order of the delegate messages is unknown and the next message could come from any connection object, there will be no concurrent execution of your delegate methods.
However, were you actually trying to use the same delegate object across multiple threads, rather than just using the asynchronous nature of the API, then you would need to deal with concurrent delegate methods.
Yes it's possible to have multiple connections. the notification object contains a pointer to the NSURLConnection that triggered the notification.
Internally I guess NSURLConnection listens to a socket and does something like this when it has data ready.
[your_delegate
performSelectorOnMainThread:#selector(connectionCallback:)
withObject:self
waitUntilDone:NO];
so you don't have to worry about it being multithreaded, NSURLConnection will take care of this. For simplicity I have written self, in the real world a NSNotification object is given.
You shouldn't have to do any checks related to multithreading.