FSEventStreamCreate bad access crash on macOS 10.15 GM

Not sure why but this crashes with bad access starting in Catalina (not on previous versions of the OS):


CFStringRef path = (__bridge CFStringRef)(self.someURL.path);

 CFArrayRef pathsToWatch = CFArrayCreate(NULL,
                                            (const void **)&path,
                                            1,
                                            NULL);

FSEventStreamRef stream;
 stream = FSEventStreamCreate(NULL,
                                     &theFunction,
                                     &context,
                                     pathsToWatch,
                                     kFSEventStreamEventIdSinceNow,
                                     1.0,
                                     flags); //<--CRASH here.


Now if I create the path CFStringRef like below instead, I do not get a crash:


NSString *nsStringP = self.someURL.path;
CFStringRef path = (__bridge CFStringRef)(nsStringP);

CFArrayRef pathsToWatch = CFArrayCreate(NULL,
                                            (const void **)&path,
                                            1,
                                            NULL);

FSEventStreamRef stream;
 stream = FSEventStreamCreate(NULL,
                                     &theFunction,
                                     &context,
                                     pathsToWatch,
                                     kFSEventStreamEventIdSinceNow,
                                     1.0,
                                     flags); //<-- no crash here.


Only difference here is that I just put the returned value from the path getter in a local variable. Not sure why the compiler wouldn't generate the same code in both cases here?

Replies

Neither one looks very safe. Perhaps a subtle change to ARC did it. I'm not sure it is a good idea to mix ARC with an API that knows nothing about it. It would be better to ensure that you have valid, properly created CFStrings that are guaranteed to live for the duration of that stream.

Using CFBridgingRetain(self.someURL.path) will work too, then just manually CFRelease it after I stop the stream. Seems to be a change in ARC.


Guess it's complicated but would think arc would/should know that self.someURL.path is not out of scope yet.


This will work too:


  NSArray *pathNS = [NSArray arrayWithObject:some.someURL.path];
   CFArrayRef pathsToWatch = CFBridgingRetain(pathNS); 

//release pathsToWatchArray after the stream is closed.

I don't even use ARC. It seemed like a nifty idea and a clear win when it first came out. But then Apple went really heavy into asynchronous blocks and suddenly ARC become a monster completely out of my control. I would rather have my app crash in development with a memory error, or throw analyze errors. Those I can fix. Otherwise, I could release an app that just leaks memory forever.