Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crash when casting CFArrayRef to NSArray

This is fine:

CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSLog(@"%@", (__bridge NSArray *)windowList);

This causes EXC_BAD_ACCESS:

CFArrayRef windowIDList = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSLog(@"Array %@", (__bridge NSArray*) windowIDList);
like image 554
ron_k Avatar asked Nov 10 '11 23:11

ron_k


2 Answers

Arrays created as NSArrays can only contain elements that act like Objective-C objects.

Arrays created as CFArrays can contain anything, if you pass the appropriate CFArrayCallBacks to CFArrayCreate.

CGWindowListCreate is creating a CFArray, and filling it with things that don't act like objects, but CGWindowListCreate is using CFArrayCallbacks that don't care about that.

When you try to print this CFArray with the %@ format specifier, NSLog sends an Objective-C -description message to the array. The CFArray handles this by sending an Objective-C description message to each of its elements. Unfortunately, its elements are not objects and so it's impossible to sending Objective-C messages to them. Thus the crash.

Try this instead:

CFStringRef description = CFCopyDescription(windowIDList);
NSLog(@"Array %@", description);
CFRelease(description);

The CFCopyDescription function uses one of the CFArrayCallbacks functions on each element of the array, instead of trying to send an Objective-C message to each one. The callback knows how to handle the array element, so it works fine. I get this output in my test program:

2011-11-10 18:50:23.888 test[15156:707] <CFArray 0x1001140c0 [0x7fff7fd24ea0]>{type = mutable-small, count = 19, values = (
    0 : <0x7d7>
    1 : <0x2d>
    2 : <0x20>
    3 : <0x21>
    4 : <0x1e>
    5 : <0x9>
    6 : <0x7a8>
    7 : <0x2c>
    8 : <0x2e>
    9 : <0x743>
    10 : <0x32>
    11 : <0x85>
    12 : <0x695>
    13 : <0x62a>
    14 : <0x62b>
    15 : <0xa>
    16 : <0x26>
    17 : <0x18>
    18 : <0x2>
)}
like image 144
rob mayoff Avatar answered Nov 12 '22 00:11

rob mayoff


From the documentation on NSArray:

NSArray is “toll-free bridged” with its Core Foundation counterpart, CFArray Reference. What this means is that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object, providing you cast one type to the other. Therefore, in an API where you see an NSArray * parameter, you can pass in a CFArrayRef, and in an API where you see a CFArrayRef parameter, you can pass in an NSArray instance. This arrangement also applies to your concrete subclasses of NSArray.

So the problem must be with the two methods being called. Again, from the documentation, CGWindowListCopyWindowInfo has return value:

An array of CFDictionaryRef types, each of which contains information about one of the windows in the current user session. If there are no windows matching the desired criteria, the function returns an empty array. If you call this function from outside of a GUI security session or when no window server is running, this function returns NULL.

and CGWindowListCreate has return value:

An array of CGWindowID values corresponding to the desired windows. If there are no windows matching the desired criteria, the function returns an empty array. If you call this function from outside of a GUI security session or when no window server is running, this function returns NULL.

When you call NSLog(@"%@",array);, the message description is sent to each object in the array. Floats, BOOLs and ints do not respond to this message. For example, you will get an error for

NSLog(@"Printing 2: %@",2);

but the error goes away if you use the int call:

NSLog(@"Printing 2: %d",2);

For your case, CGWindowListCreate is returning an array of CGWindowID values, and these are 32 bit unsigned integers. Therefore they do no respond to %@, but will respond to %u. Hence the fix is to print the array manually using %u.

like image 32
PengOne Avatar answered Nov 11 '22 23:11

PengOne