Question

I'm using FSEvents to monitor a directory, and whenever the directory changes I call a block which I originally passed into the FSEventStreamContext of the FSEventStreamRef. How do I release the block when it is time to stop monitoring the directory? Code below for reference.

void fsevents_callback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) {
    void (^block)() = (__bridge void (^)())(clientCallBackInfo);
    block();
}

- (FSEventStreamRef)startObserving:(NSString *)path block:(void(^)())block {
    void *ptr = (void *)CFBridgingRetain(block);  // NOTE: the block is retained
    FSEventStreamContext context = { 0, ptr, NULL, NULL, NULL };
    FSEventStreamRef stream = FSEventStreamCreate(NULL, fsevents_callback, &context, (__bridge CFArrayRef)@[path], kFSEventStreamEventIdSinceNow, 10, kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagIgnoreSelf);
    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    FSEventStreamStart(stream);
    return stream;
}

- (void)stopObserving:(FSEventStreamRef)stream {
    // HELP: the block should be released here. can I get it through FSEvents?
    FSEventStreamStop(stream);
    FSEventStreamInvalidate(stream);
    FSEventStreamRelease(stream);
}
Was it helpful?

Solution

FSEventStreamContext has member variables for functions to retain and release the info pointer, which in your example is your void * block pointer.

Via Apple's FSEvents reference:

retain
  The callback used retain the info pointer. This can be NULL.

release
  The callback used release a retain on the info pointer. This can be NULL.

First, retain. Since you need to cast the block to void * for FSEventStreamContext anyway, I think it's fine to continue using CFBridgingRetain() in your startObserving: method. No retain callback function is needed.

For release, try this callback function:

void release_callback(const void *info) {
    CFBridgingRelease(info);
}

Then try changing your FSEventStreamContext declaration to:

    FSEventStreamContext context = { 0, ptr, NULL, release_callback, NULL };

That should release your block when stopObserving: is called.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top