I was wondering if there's any limitation for the context where I initialize my xpc service.
Here's how I currently initialize my xpc service from main() which works just fine.
listener_ = [[NSXPCListener alloc]
initWithMachServiceName:#"com.bla.bla"];
xpcService *delegate = [xpcService new];
listener_.delegate = delegate;
[listener_ resume];
[[NSRunLoop mainRunLoop] run];
However, when calling it from different method(main)/thread(main thread)... It doesn't accept remote calls, even though the listener was properly initialized.
I even tried to wrap this code to run on the main thread using the following wrapper
dispatch_sync(dispatch_get_main_queue(), ^{
listener_ = [[NSXPCListener alloc]
initWithMachServiceName:#"com.bla.bla"];
xpcService *delegate = [xpcService new];
listener_.delegate = delegate;
[listener_ resume];
}
In the example above, the [[NSRunLoop mainRunLoop] run]; is called from the main method.
So my question is what are the requirements to make the XPC work.. is it mandatory to call it from the main method ?
Related
Very similar issue is already discussed here. The problem at hand and what I am trying to achieve is to call a function on a given object in the thread it is created at. Here is the complete case:
an instance of class A is created in a given NSThread (Thread A) (not the main one). The instance keeps its creating NSThread as a member variable.
an instance of class B has one of its member functions executing in another NSThread - Thread B, and wants to call a function of A in A's creation thread. Thus B's currently executing function issues the following call:
[_a performSelector: #(fun)
onThread: _a.creationThread
withObject: nil
waitUntilDone: NO];
If the creation thread of A's instance is not the main one, fun never gets called. If the creation thread is the main one it is always called. First I was thinking whether the thread that created A's instance has been destroyed and the pointer points to an invalid thread but actually calling any functions on the thread object (Thread A) produces valid results and no crashes. Also checking the object is valid according to this check. Any suggestions?
Update:
What I'm doing is creating a timer on a background thread:
_timer = [NSTimer timerWithTimeInterval:60.0 target:self selector:#selector(fun:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
This code is the one starting the timer. The timer is not started in any specific background thread. It just happens that the function that creates the timer could be called in any thread. Thus the timer should be invalidated in the exactly same one as the NSTimer documentation states: "you should always call the invalidate method from the same thread on which the timer was installed."
To run timer on background thread, you have two options.
Use dispatch timer source:
#property (nonatomic, strong) dispatch_source_t timer;
and you can then configure this timer to fire every two seconds:
- (void)startTimer {
dispatch_queue_t queue = dispatch_queue_create("com.domain.app.timer", 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
// call whatever you want here
});
dispatch_resume(self.timer);
}
- (void)stopTimer {
dispatch_cancel(self.timer);
self.timer = nil;
}
Run NSTimer on background thread. To do this, you can do something like:
#property (atomic) BOOL shouldKeepRunning;
#property (nonatomic, strong) NSThread *timerThread;
#property (nonatomic, strong) NSTimer *timer;
And
- (void)startTimerThread {
self.timerThread = [[NSThread alloc] initWithTarget:self selector:#selector(startTimer:) object:nil];
[self.timerThread start];
}
- (void)stopTimerThread {
[self performSelector:#selector(stopTimer:) onThread:self.timerThread withObject:nil waitUntilDone:false];
}
- (void)startTimer:(id)__unused object {
#autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:YES];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
self.shouldKeepRunning = YES;
while (self.shouldKeepRunning && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
;
self.timerThread = nil;
}
}
- (void)handleTimer:(NSTimer *)timer {
NSLog(#"tick");
}
- (void)stopTimer:(id)__unused object {
[self.timer invalidate];
self.timer = nil;
self.shouldKeepRunning = FALSE;
}
I'm not crazy about the shouldKeepRunning state variable, but if you look at the Apple documentation for the run method, they discourage the reliance upon adding sources/timers to run loops:
If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
Personally, I'd recommend the dispatch timer approach.
i'm trying to establish an FTP connection within an app. i want to upload several files to a FTP server, all files in one directory. So at first i want to create the remote directory.
- (void) createRemoteDir {
NSURL *destinationDirURL = [NSURL URLWithString: uploadDir];
CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
assert(writeStreamRef != NULL);
ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
BOOL success = [ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName];
if (success) {
NSLog(#"\tsuccessfully set the user name");
}
success = [ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword];
if (success) {
NSLog(#"\tsuccessfully set the password");
}
ftpStream.delegate = self;
[ftpStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// open stream
[ftpStream open];
}
This code doesn't work when executed in a background thread using the following call:
[self performSelectorInBackground: #selector(createRemoteDir) withObject: nil];
my guess is that the (background-threads) runloop isn't running?
If i send the message inside the main thread the uploading just works fine:
[self createRemoteDir];
as the runloop of the main thread is up and running.
but fairly large files are going to be uploaded; so i want to put that workload in a background thread.
but how and where do i set up the NSRunLoop, so that the whole uploading happens in a background thread? Apples documentation on NSRunLoops (especially how to start them without using a timer/input source, as in this case) didn't help me out.
I found/created a solution that at least works for me.
with the above method (createRemoteDir), the following code applied and worked for me:
NSError *error;
createdDirectory = FALSE;
/*
only 'prepares' the stream for upload
- doesn't actually upload anything until the runloop of this background thread is run
*/
[self createRemoteDir];
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
do {
if(![currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]) {
// log error if the runloop invocation failed
error = [[NSError alloc] initWithDomain: #"org.mJae.FTPUploadTrial"
code: 23
userInfo: nil];
}
} while (!createdDirectory && !error);
// close stream, remove from runloop
[ftpStream close];
[ftpStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
if (error) {
// handle error
}
It runs in a background thread and creates the directory on the ftp server.
I like it more than other examples where runloops are only run for an assumed small interval, say 1second.
[NSDate distantFuture]
is a date in the futur (several centuries, according to Apples documentation). But that's good as the "break-condition" is handled by my class property createdDirectory - or the occurance of an error while starting the runloop.
I can't explain why it works without me explicitly attaching an input source to the runloop (NSTimer or NSPort), but my guess is, it is sufficient that the NSOutputStream is scheduled in the runloop of the background thread (see createRemoteDir).
You could also try to use a dispatch_async call to perform your createRemoteDir in the background. It's much simpler to use and you won't have to worry about managing extra threads.
Here's what the code would look like:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self createRemoteDir];
});
I am am trying to get multiple NSURLConnections to run in parallel (synchronously), however if it is not running on the main thread (block of code commented out below) the URL connection doesn't seem to work at all (none of the NSURLConnection delegate methods are triggered). Here is the code I have (implementation file of an NSOperation subclass):
- (void)start
{
NSLog(#"DataRetriever.m start");
if ([self.DRDelegate respondsToSelector:#selector(dataRetrieverBeganExecuting:)])
[self.DRDelegate dataRetrieverBeganExecuting:identifier];
if ([self isCancelled]) {
[self finish];
} else {
/*
//If this block is not commented out NSURLConnection works, but not otherwise
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}*/
SJLog(#"operation for <%#> started.", _url);
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURLRequest * request = [NSURLRequest requestWithURL:_url];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
} //not cancelled
}//start
Ran through it with a debugger, and after the end of this start method none of the NSURLConnection delegates trigger (I set breakpoints there). But on the main thread it works just fine. Any ideas of what's up? Thanks!
Background threads don't automatically have an active run loop on them. You need to start up the run loop after you create the NSURLConnection in order to get any input from it. Fortunately, this is quite simple:
[[NSRunLoop currentRunLoop] run];
When you say that you are running the connections synchronously, you are incorrect. The default mode of NSURLConnection is asynchronous -- it creates and manages a new background thread for you, and calls back to the delegate on the original thread. You therefore don't need to worry about blocking the main thread.
If you do actually want to perform a synchronous connection, you would use sendSynchronousRequest:returningResponse:error:, which will directly return the data. See "Downloading Data Synchronously" for details.
NSURLConnection needs an active run loop to actually work; the easiest way to ensure this is to just run it from the main thread.
Note that NSURLConnection normally runs asynchronously (and if you run one synchronously, what it really does is run one asynchronously on another thread and then block until that completes), so except for whatever processing you do in your delegate methods it shouldn't have much of an effect on UI responsiveness.
I'm trying to create a thread that configures a run loop to run a physics engine through a defined NSTimer. However, I'm having trouble making the thread exit normally (or I think the problem is).
Attached are the relevant portions of my code:
(This code is in a view controller)
(back is called when a button is pressed)
- (void)back {
[timestep invalidate];
exiting = YES;
[self release];
}
- (void)initializePhysicsWorld {
// Initializes the thread to simulate physics interactions.
[NSThread detachNewThreadSelector:#selector(physicsThreadMethod)
toTarget:self
withObject:nil];
}
- (void)physicsThreadMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
timestep = [NSTimer timerWithTimeInterval:1.0f/60.0f
target:self
selector:#selector(step:)
userInfo:nil
repeats:YES];
[myRunLoop addTimer:timestep forMode:NSDefaultRunLoopMode];
while (!exiting) {
CFRunLoopRun();
[pool release];
pool = [[NSAutoreleasePool alloc] init]; // periodically refreshes pool
}
CFRunLoopStop([myRunLoop getCFRunLoop]);
NSLog(#"Thread is going to exit");
[pool release];
}
- (void)dealloc {
if ([self.view superview]) {
[self.view removeFromSuperview];
}
[super dealloc];
}
The engine (the step: function) runs fine, but when I try to exit the loop by running the method back, it would appear that the thread does not release its retain on my view controller (dealloc is not called). I think my thread didn't exit the physicsThreadMethod method as the NSLog does not appear in the console. Dealloc was only called when I run 'back' a second time.
I'm not really sure why this is happening, so I would really appreciate any help. Thanks!
The problem lies in here:
while (!exiting) {
CFRunLoopRun(); //<-- here you start the run loop.
// the lines under this line are NEVER executed. also the while loop does nothing
[pool release];
pool = [[NSAutoreleasePool alloc] init]; // periodically refreshes pool
}
You could use NSOperations to wrap your work, there are already properties defined on this class to do exactly this kind of thing.
If you want to stick with your implementation in a NSThread you have to take a look at the CFRunLoop reference and how to add observers to the run loop.
Does CFRunLoopRun return every step:? CFRunLoopStop or [myRunLoop runUntilDate:] would help.
I have a piece of network code that uses AsyncSocket but moves it to a separate runloop. I'm creating this runloop with the following piece of code:
[NSThread detachNewThreadSelector:#selector(_workerLoop) toTarget:self withObject:nil];
and here's how my _workerLoop looks like (they're both in the same class):
-(void)_workerLoop {
workerLoop = [[NSRunLoop currentRunLoop] retain];
while(keepWorkerLoopRunning) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[workerLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.5f]];
[pool release];
}
[workerLoop release];
workerLoop = nil;
}
Now, according to the docs, the NSThread will retain the target, and as this thread will only terminate when AsyncSocket disconnects, it's impossible to release and deallocate this object until the socket disconnects.
How can I fix this or maybe I'm doing something wrong?
I've solved this by refactoring the runloop constructor out into own class, referenced by parent class that handles the networking code. This way, parent object is being deallocated, it can stop the thread and release the runloop