Mixing Objective-C with C and code organization - objective-c

I'm working on a desktop application that watch folders using the fileevent api, so basically this is my code :
#import "PNAppDelegate.h"
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
[(__bridge PNAppDelegate *)clientCallBackInfo reloadStatus];
};
#implementation PNAppDelegate
#synthesize window = _window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSArray *pathsToWatch = [NSArray arrayWithObject: #"/Users/romainpouclet/Projects/foo"];
void *appPointer = (__bridge void *)self;
FSEventStreamContext context = {0, appPointer, NULL, NULL, NULL};
FSEventStreamRef stream;
CFAbsoluteTime latency = 3.0;
stream = FSEventStreamCreate(NULL,
&callback,
&context,
(__bridge CFArrayRef) pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagNone);
NSLog(#"Schedule with run loop");
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
[self reloadStatus];
}
-(void)reloadStatus
{
}
#end
No problem, it works pretty well for a POC as simple as this one, BUT it feels kinda ugly (and it probably is, I'm not really used to mix Objective-C and C). So here are my questions :
where should I declare my callback? It feels weird having it at the top of my file, just because it worked there.
is it possible to have some kind of #selector-based approach instead of callbacks? (I find them reassuring :D)
Thanks for your time !

Why not put the callback declaration in either PNAppDelegate.h, or its own header file (if you don't want to spread it around your app). That way you can just include the header file and put the function definition anywhere you want. Doing so is standard C functionality.
// Header file callback.h
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]);
// PNAppDelegate.m
#import "PNAppDelegate.h"
#import "callback.h"
#implementation PNAppDelegate
...
#end
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
[(__bridge PNAppDelegate *)clientCallBackInfo reloadStatus];
};

You are correct, that code IS ugly. However, bridging C and Obj-C is no small task, so you really only have a few options:
Create an Objective-C wrapper around the C-based API. This would be my recommended approach, especially if the API is not too complex. It gives you the advantage of using either delegates or blocks, instead of functions.
Use blocks for callbacks, by getting their internal function pointer:
// internal structure of a block
struct blockPtr {
void *__isa;
int __flags;
int __reserved;
void *__FuncPtr;
void *__descriptor;
};
int main()
{
#autoreleasepool {
__block int b = 0;
void (^blockReference)(void *) = ^(void *arg) {
NSLog(#"<%s>: %i", arg, b++);
};
void *blockFunc = ((__bridge struct blockPtr *) blockReference)->__FuncPtr;
void (*castedFunction)(void *, void *) = blockFunc;
// the first argument to any block funciton is the block
// reference itself, similar to how the first argument to
// any objc function is 'self', however, in most cases you
// don't need the block reference (unless reading __block variables), it's just difficult to
// get that first argument from inside the block
castedFunction((__bridge void *) blockReference, "one");
castedFunction((__bridge void *) blockReference, "two");
}
}
I really don't think this is practical in most situations, but if you can find a way to make it work, more power to you.
Stick with how you are currently doing it. It sucks, but that is how C works.

Related

Why Does This Objective C/C++ Code Require main.m instead of main.mm?

I get strange code errors when I rename the following command line program from main.m to main.mm. Works just fine as main.m. Anyone know why?
https://stackoverflow.com/a/36469891/105539
SOURCE
#import <Foundation/Foundation.h>
void detectNewFile (
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
int i;
char **paths = eventPaths;
printf("GOT AN EVENT!!!!\n");
for (i=0; i<numEvents; i++) {
printf("Change %llu in %s, flags %u\n", eventIds[i], paths[i], (unsigned int)eventFlags[i]);
}
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
short nPathCount = 2;
CFStringRef mypath[nPathCount];
mypath[0] = CFSTR("/Users/mike/Documents");
mypath[1] = CFSTR("/Users/mike/Downloads");
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, nPathCount, NULL);
void *callbackInfo = NULL;
CFAbsoluteTime latency = 1.0; // seconds
FSEventStreamRef hStream = FSEventStreamCreate(NULL,
&detectNewFile,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagFileEvents
);
FSEventStreamScheduleWithRunLoop(hStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(hStream);
printf("Waiting on new file creations...\n");
CFRunLoopRun(); // runs in an endless loop, only letting the callback function run
} // end autorelease pool
return 0;
}
ERRORS
FOR:
char **paths = eventPaths;
Cannot initialize a variable of type 'char **' with an lvalue of type 'void *'
FOR:
FSEventStreamRef hStream = FSEventStreamCreate(NULL,
&detectNewFile,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagFileEvents
);
No matching function for call to 'FSEventStreamCreate'
Thanks to #johnelemans, I found the problems. In C, it's legal to have automatic casting from void * to char **, but not in C++, which is what the .mm file would switch this into. The fix is to use casting:
char **paths = (char **)eventPaths;
Then, on the FSEventStreamCreate, it didn't like the void * instead of this:
FSEventStreamContext *callbackInfo = NULL;
...and didn't like the CFAbsoluteTime instead of:
CFTimeInterval latency = 1.0; // seconds
Then, you need to add CoreServices.framework library to the build steps.
I made those changes and it compiles now.

Watching for FileSystem Events on MacOS 10.10

I'm trying to code a little command line tool using Xcode (under MacOS 10.10) that watches a specific folder and informs me about changes to files in that folder.
I'm following the guide given in https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html
Here is my current code:
#import <Foundation/Foundation.h>
#include <CoreServices/CoreServices.h>
void mycallback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
int i;
char **paths = eventPaths;
printf("Callback called\n");
for (i=0; i<numEvents; i++) {
int count;
/* flags are unsigned long, IDs are uint64_t */
printf("Change %llu in %s, flags %lu\n", eventIds[i], paths[i], eventFlags[i]);
}
}
int main(int argc, const char * argv[]) {
// #autoreleasepool {
// insert code here...
NSLog(#"Starting to watch ");
/* Define variables and create a CFArray object containing
CFString objects containing paths to watch.
*/
CFStringRef mypath = CFSTR("/Users/testuser/");
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
void *callbackInfo = NULL; // could put stream-specific data here.
FSEventStreamRef stream;
CFAbsoluteTime latency = 3.0; /* Latency in seconds */
/* Create the stream, passing in a callback */
stream = FSEventStreamCreate(NULL,
&mycallback,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow, /* Or a previous event ID */
latency,
kFSEventStreamCreateFlagNone /* Flags explained in reference */
);
/* Create the stream before calling this. */
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
CFRunLoopRun();
return 0;
}
The code compiles and can be started. However, no events are being fired. I'm pretty new to Xcode and also never ever used a callback function before. So I guess that this is a pretty dumb mistake that I made.
I'd appreciate any hints that may help.
Thanks in advance
Norbert
Update: The code was updated with a working solution from the answer.
According to the documentation, after starting event stream to send events, you should call CFRunLoopRun.
Try to change your while() loop to:
CFRunLoopRun();
Update. My output:
$ ./fsevent
2015-05-17 13:51:29.718 fsevent[898:23601] Starting to watch
Callback called
Change 1165579 in /Users/baf/src/tests/, flags 66560
Callback called
Change 1165594 in /Users/baf/src/tests/, flags 66048
It appears you have FSEventStreamRef stream; commented out. In order to start watching events you'll want to uncomment that line. It also looks like your callback print statement is also commented out, although that appears to be just for debugging perhaps.

Programmatically get all frame variables in objective-c

I'm trying to create my own custom assert. However, I would like my assertion to automatically include all of the relevant variables. This seems really basic to me, and I've searched around for about an hour but I can't seem to find a way get access to all the relevant stack frame variables. Does anyone know how to get these variables?
FYI - I don't need to access the variables in the debugger, I need to access them programmatically. I would like to upload them along with the crash report to give me more information about the crash. I also know that I can print them out manually...that is exactly what I'm looking to avoid.
You are basically asking to re-invent a good sized chunk of the debugger.
Without symbols, there isn't anything you can interrogate to figure out the layout of the local frame. Even with symbols, it is quite likely that the optimizer will have stomped on any local variables as the optimizer will re-use stack slots at whim once it determines the variable is no longer needed within the frame.
Note that many crashes won't be able to be caught at all or, if caught, the frame within which they occurred will have long since been destroyed.
Since you mention that you are creating a custom assertion, it sounds like you really aren't looking to introspect crashes as much as dump a snap of the local frame when you programatically detect that things have gone off the rails. While there really isn't a means of automatically reporting on local stack state, you could do something like:
{ ... some function ....
... local variables ...
#define reportblock ^{ ... code that summarizes locals ... ; return summary; }
YourAssert( cond, "cond gone bad. summary: %#", reportblock());
}
Note that the #define ensures that each YourAssert() captures the state at the time of the assertion. Note also that the above might have a potentially significant impact on performance.
Note also that I just made that code up. It seems like it is worthy of investigation, but may prove non-viable for a number of reasons.
If you're willing to use Objective-C++, then this is definitely a possibility, as long as you are also willing to declare your variables differently, and understand that you will only be able to grab your own variables with this method.
Also note that it will increase your stack frame size with extra __stack_ variables, which could cause memory issues (although I doubt it, personally).
It won't work with certain constructs such as for-loops, but for 95% of cases, this should work for what you need:
#include <vector>
struct stack_variable;
static std::vector<const stack_variable *> stack_variables;
struct stack_variable {
void **_value;
const char *_name;
const char *_type;
const char *_file;
const char *_line;
private:
template<typename T>
stack_variable(const T& value, const char *type, const char *name, const char *file, const char *line) : _value((void **) &value), _type(type), _name(name), _file(file), _line(line) {
add(*this);
}
static inline void add(const stack_variable &var) {
stack_variables.push_back(static_cast<const stack_variable *>(&var));
}
static inline void remove(const stack_variable &var) {
for (auto it = stack_variables.begin(); it != stack_variables.end(); it++) {
if ((*it) == &var) {
stack_variables.erase(it);
return;
}
}
}
public:
template<typename T>
static inline stack_variable create(const T& value, const char *type, const char *name, const char *file, const char *line) {
return stack_variable(value, type, name, file, line);
}
~stack_variable() {
remove(*this);
}
void print() const {
// treat the value as a pointer
printf("%s:%s - %s %s = %p\n", _file, _line, _type, _name, *_value);
}
static void dump_vars() {
for (auto var : stack_variables) {
var->print();
}
}
};
#define __LINE_STR(LINE) #LINE
#define _LINE_STR(LINE) __LINE_STR(LINE)
#define LINE_STR _LINE_STR(__LINE__)
#define LOCAL_VAR(type, name, value)\
type name = value;\
stack_variable __stack_ ## name = stack_variable::create<type>(name, #type, #name, __FILE__, LINE_STR);\
(void) __stack_ ## name;
Example:
int temp() {
LOCAL_VAR(int, i_wont_show, 0);
return i_wont_show;
}
int main(){
LOCAL_VAR(long, l, 15);
LOCAL_VAR(int, x, 192);
LOCAL_VAR(short, y, 256);
temp();
l += 10;
stack_variable::dump_vars();
}
Output (note the junk extra bytes for the values smaller than sizeof(void *), there isn't much I can do about that):
/Users/rross/Documents/TestProj/TestProj/main.mm:672 - long l = 0x19
/Users/rross/Documents/TestProj/TestProj/main.mm:673 - int x = 0x5fbff8b8000000c0
/Users/rross/Documents/TestProj/TestProj/main.mm:674 - short y = 0xd000000010100
Threads will royally screw this up, however, so in a multithreaded environment this is (almost) worthless.
I decided to add this as a separate answer, as it uses the same approach as my other one, but this time with an all ObjC code. Unfortunately, you still have to re-declare all of your stack variables, just like before, but hopefully now it will work better with your existing code-base.
StackVariable.h:
#import <Foundation/Foundation.h>
#define LOCAL_VAR(p_type, p_name, p_value)\
p_type p_name = p_value;\
StackVariable *__stack_ ## p_name = [[StackVariable alloc] initWithPointer:&p_name\
size:sizeof(p_type)\
name:#p_name\
type:#p_type\
file:__FILE__\
line:__LINE__];\
(void) __stack_ ## p_name;
#interface StackVariable : NSObject
-(id) initWithPointer:(void *) ptr
size:(size_t) size
name:(const char *) name
type:(const char *) type
file:(const char *) file
line:(const int) line;
+(NSString *) dump;
#end
StackVariable.m:
#import "StackVariable.h"
static NSMutableArray *stackVariables;
#implementation StackVariable {
void *_ptr;
size_t _size;
const char *_name;
const char *_type;
const char *_file;
int _line;
}
-(id) initWithPointer:(void *)ptr size:(size_t)size name:(const char *)name type:(const char *)type file:(const char *)file line:(int)line
{
if (self = [super init]) {
if (stackVariables == nil) {
stackVariables = [NSMutableArray new];
}
_ptr = ptr;
_size = size;
_name = name;
_type = type;
_file = file;
_line = line;
[stackVariables addObject:[NSValue valueWithNonretainedObject:self]];
}
return self;
}
-(NSString *) description {
NSMutableString *result = [NSMutableString stringWithFormat:#"%s:%d - %s %s = { ", _file, _line, _type, _name];
const uint8_t *bytes = (const uint8 *) _ptr;
for (size_t i = 0; i < _size; i++) {
[result appendFormat:#"%02x ", bytes[i]];
}
[result appendString:#"}"];
return result;
}
+(NSString *) dump {
NSMutableString *result = [NSMutableString new];
for (NSValue *value in stackVariables) {
__weak StackVariable *var = [value nonretainedObjectValue];
[result appendString:[var description]];
[result appendString:#"\n"];
}
return result;
}
-(void) dealloc {
[stackVariables removeObject:[NSValue valueWithNonretainedObject:self]];
}
#end
Example:
#include "StackVariable.h"
int temp() {
LOCAL_VAR(int, i_wont_show, 0);
return i_wont_show;
}
int main(){
LOCAL_VAR(long, l, 15);
LOCAL_VAR(int, x, 192);
LOCAL_VAR(short, y, 256);
temp();
l += 10;
puts([[StackVariable dump] UTF8String]);
}
Output:
/Users/rross/Documents/TestProj/TestProj/main.m:676 - long l = { 19 00 00 00 00 00 00 00 }
/Users/rross/Documents/TestProj/TestProj/main.m:677 - int x = { c0 00 00 00 }
/Users/rross/Documents/TestProj/TestProj/main.m:678 - short y = { 00 01 }
This requires ARC (and all of it's magic) enabled for any file you want to test this in, or you will manually have to release the __stack_ variables, which won't be pretty.
However, it now gives you a hex dump of the variable (rather than the weird pointer one), and if you really tried hard enough (using __builtin_types_compatible), it could detect whether the result was an object, and print that.
Once again, threads will mess this up, but a simple way to fix that would be to create a NSDictionary of NSArrays, with a NSThread as the key. Makes it a bit slower, but let's be honest, if you're using this over the C++ version, you aren't going for performance.

Handling File System Events in OSX

So im using the EventStream to watch a folder for change. Now it all works fine and I can see a log call back when I alter files in the folder, but I cant seem to call my folderWatch, it gives the error "use of undeclared identifier 'self'". I can use this function everywhere else, just not in the fsEventsCallback. Any help would be appreciated!
void fsEventsCallback(ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]){
[self folderWatch];
NSLog(#"2");
}
The reason is that fsEventsCallback is a C function and not an Objective-C instance method, so fsEventsCallback does not know anything about self.
You can use the info field in the FSEventStreamContext to pass self to the callback function. The following example assumes that your class is called Watcher.
(If you don't use ARC, you can omit all the __bridge casts.)
- (void)folderWatch
{
}
void fsEventsCallback(ConstFSEventStreamRef streamRef,
void *info,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
Watcher *watcher = (__bridge Watcher *)info;
[watcher folderWatch];
}
- (void)startWatching
{
FSEventStreamContext context;
context.info = (__bridge void *)(self);
context.version = 0;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
NSArray *pathsToWatch = #[#"/path/to/watch"];
self.eventStream = FSEventStreamCreate(NULL,
&fsEventsCallback,
&context,
(__bridge CFArrayRef)(pathsToWatch),
kFSEventStreamEventIdSinceNow,
1.0,
kFSEventStreamCreateFlagFileEvents
);
}

Checking Objective-C block type?

This is primarily a curiosity, I'm not really sure what's the practical use of this but here goes.
Since blocks are also Objective-C objects, is it possible to check their type? That is, does it respond to the isKindOfClass: message and how to use that message with respect to blocks?
My naive thought that it's probably like this:
-(void) aMethod {
typedef int (^BlockA)(int x, int y);
id blockVar = ...; // get a block from somewhere
if([blockVar isKindOfClass:BlockA]) {
BlockA blockVarA = blockVar;
int result = blockVarA(1,2);
}
}
The code above probably won't work. But if it is possible to check a block's type, what is the correct way to do it?
Can do, kinda sorta.
But first, let's disambiguate. -[NSObject isKindOfClass:] can tell you it's a block, and that's about it. E.g. I believe this line of code -- ostensibly & unfortunately A BAD IDEA -- will return YES for blocks on present Lion & iOS 5.x:
[myBlock isKindOfClass:NSClassFromString(#"NSBlock")]
That won't help you distinguish the block's function signature.
But it can be done, by snagging the signature from the block's documented internal struct. Code follows for an example OS X command-line app, much of which ripped from Mike Ash's MABlockClosure (great detailed explanation). (UPDATE: Github project CTObjectiveCRuntimeAdditions also apparently provides library code for just this purpose.)
#import <Foundation/Foundation.h>
struct BlockDescriptor {
unsigned long reserved;
unsigned long size;
void *rest[1];
};
struct Block {
void *isa;
int flags;
int reserved;
void *invoke;
struct BlockDescriptor *descriptor;
};
static const char *BlockSig(id blockObj)
{
struct Block *block = (void *)blockObj;
struct BlockDescriptor *descriptor = block->descriptor;
int copyDisposeFlag = 1 << 25;
int signatureFlag = 1 << 30;
assert(block->flags & signatureFlag);
int index = 0;
if(block->flags & copyDisposeFlag)
index += 2;
return descriptor->rest[index];
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
int (^block)(NSNumber *) = ^(NSNumber *num) {
NSLog(#"%# %#", NSStringFromClass([num class]), num);
return [num intValue];
};
NSLog(#"signature %s", BlockSig(block));
NSLog(#"retval %d", (int)block([NSNumber numberWithInt:42]));
}
return 0;
}
Run this and you should get something like:
[58003:403] signature i16#?0#8
[58003:403] __NSCFNumber 42
[58003:403] retval 42
The numbers in the signature (I'm told they are offsets) can be stripped for simpler i#?#.
The signature is in the #encode format, which isn't perfect (e.g. most objects map to same #), but should afford you some ability to distinguish blocks with different signatures at runtime.
While it's not documented in the Apple link, my testing points to #? being the code for a block type, which makes sense of the signature above. I found a clang-developers discussion on this issue which seems to back this up.
The "BlockA" in (^BlockA) is the variable name (in this case a typedef), not its class.
Blocks are objects, but not regular subclasses of NSObject. They only implement a subset of the methods. -isKindOfClass: will probably just crash.
Blocks are of the type NSMallocBlock or NSConcreteGlobalBlock, ... depending on where they were created (heap, stack, ...).
It seems that blocks are of classes like __NSGlobalBlock__, __NSStackBlock__, or __NSMallocBlock__, etc., whose inheritance chain eventually goes to NSBlock and then NSObject. So you could test whether something is a block by doing [... isKindOfClass:NSClassFromString(#"NSBlock")]. However, there doesn't seem to be any way to query a block's signature (return type and argument types) at runtime, so you wouldn't be able to distinguish between blocks of different signatures.
As well as Apple having nothing I can find to say on the matter, poking at a block with class_copyMethodList and method_getName reveals no obvious exposed methods. So I'm going to say that it isn't possible to check their type.
A old question, but anyway:
If you want a simple way of doing this: (Compile it with -fno-objc-arc)
Class __NSGlobalBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_block_t thisIsAGlobalBlock = ^{// a block with no variables will be a __NSGlobalBlock__
};
result = [[thisIsAGlobalBlock class] retain];
});
return result;
};
Class __NSStackBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block dispatch_block_t thisIsAStackBlock = ^{
return ;// we really DON'T want infinate recursion
thisIsAStackBlock();// including a reference to __block var makes this a __NSStackBlock__
};
result = [[thisIsAStackBlock class] retain];
});
return result;
};
Class __NSMallocBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block dispatch_block_t thisIsAMallocBlock = Block_copy(// << turns the __NSStackBlock__ Block into a __NSMallocBlock__
^{
return ;// we really DON'T want infinate recursion
thisIsAMallocBlock();// including a reference to __block var makes this a __NSStackBlock__
});
result = [[thisIsAMallocBlock class] retain];
Block_release(thisIsAMallocBlock);
});
return result;
};
Test Code:
#autoreleasepool {
__block dispatch_block_t iAmAGlobalBlock = ^{
};
__block dispatch_block_t iAmAStackBlock = ^{
return;
iAmAStackBlock();
};
dispatch_block_t iAmHeapBlock = Block_copy(iAmAStackBlock);
dispatch_block_t iAmNotAHeapBlock = Block_copy(iAmAGlobalBlock);
if ([iAmAGlobalBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {
NSLog(#"very great success!");
}
if ([iAmAStackBlock isKindOfClass:__NSStackBlock__CLASS()]) {
NSLog(#"another great success!");
}
if ([iAmHeapBlock isKindOfClass:__NSMallocBlock__CLASS()]) {
NSLog(#"also great success!");
}
if ([iAmNotAHeapBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {
NSLog(#"yet another great success!");
}
NSLog (#"Block classes, as reported by NSStringFromClass():\n__NSGlobalBlock__CLASS() = %#\n__NSStackBlock__CLASS() = %#\n__NSMallocBlock__CLASS() = %#\n[iAmAGlobalBlock class] = %#\n[iAmAStackBlock class] = %#\n[iAmHeapBlock class] = %#\n[iAmNotAHeapBlock class] = %#\n",
NSStringFromClass(__NSGlobalBlock__CLASS()),
NSStringFromClass(__NSStackBlock__CLASS()),
NSStringFromClass(__NSMallocBlock__CLASS()),
NSStringFromClass([iAmAGlobalBlock class]),
NSStringFromClass([iAmAStackBlock class]),
NSStringFromClass([iAmHeapBlock class]),
NSStringFromClass([iAmNotAHeapBlock class])
);
Block_release(iAmHeapBlock);
Block_release(iAmNotAHeapBlock);// not really needed, but since we did "Block_copy" it...
}