Well I'm completely stumped - I cannot cal from Mono into Obj-C code using Selectors either. So as a last ditch attempt I'm posting the code:
#implementation MonoWrapper
- (id)init {
self = [super init];
if (self) {
NSBundle *main = [NSBundle mainBundle];
NSString *path = [main bundlePath];
const char *c_path = [path UTF8String];
[main autorelease];
[path autorelease];
chdir (c_path);
setenv ("MONO_PATH", c_path, 1);
setenv ("MONO_XMLSERIALIZER_THS", "no", 1);
setenv ("DYLD_BIND_AT_LAUNCH", "1", 1);
setenv ("MONO_REFLECTION_SERIALIZER", "yes", 1);
_domain = mono_jit_init_version ("MonoTouch", "v2.0.50727");
MonoAssembly *assembly = mono_assembly_open("PhoneGap.dll", NULL);
MonoImage *image = mono_assembly_get_image(assembly);
MonoClass *class = mono_class_from_name(image, "PhoneGap", "PhoneGap");
MonoMethodDesc *methodDesc = mono_method_desc_new("PhoneGap.PhoneGap:getInt", TRUE);
_callbackMethod = mono_method_desc_search_in_class(methodDesc, class);
/* allocate memory for the object */
_instance = mono_object_new (_domain, class);
/* execute the default argument-less constructor */
mono_runtime_object_init (_instance);
}
// Done
return self;
}
- (void)DoSomething {
int jim = 0;
}
- (int)multiplyA:(int)a {
void *params[] = { self, #selector(DoSomething), &a };
MonoObject *result = mono_runtime_invoke(_callbackMethod, _instance, params, NULL);
int n = *(int*)mono_object_unbox (result);
return n;
}
#end
And in MonoTouch:
using System;
using MonoTouch.ObjCRuntime;
namespace PhoneGap
{
public class PhoneGap
{
public PhoneGap ()
{
}
public int getInt(IntPtr instance, IntPtr sel, int val) {
Messaging.void_objc_msgSend (instance, sel);
return val * 2;
}
}
}
Can anyone tell me how to get the Target instance handle in Mono and how to get the Selector?
Thanks
James
The target/instance IntPtr is a pointer to the native instance. You would get this when you alloc the class.
For class/static method, the target/instance IntPtr is the native class descriptor. You can get this by creating a MonoMac.ObjcRuntime.Class and using its Handle property to get the native class descriptor.
The selector IntPtr is a pointer to a selector. You can get this by creating a MonoMac.ObjcRuntime.Selector and using its Handle property to get the native selector.
When creating a wrapper class for an NSObject, you would need to subclass NSObject and use the MonoMac.Foundation.Register attribute to set its Objc-C name. Then, when you new up the wrapper, it would alloc and init the underlying native instance and you'd be able to get that from the NSObject's Handle property. This also means that you can "unwrap" pointers to NSObjects to get the 1:1 managed wrapper using MonoMac.ObjcRuntime.Runtime.GetNSObject (IntPtr ptr).
In general, you're probably better off using the btouch tools to generate the binding for you.
Related
I have a project with a lot of classes.
I want to log (e.g. to stderr) invocations of each selector in runtime.
My main requirement is not to change the existing code, so I can't just log the function's params at the start of each call.
If some method is invoked during program execution, e.g.
#implementation Class1
// ...
- (int)someFunc:(Class2*) a andClass3:(Class3*)b
{
}
// ...
#end
I want to replace it with something like:
- (int)someFuncWrapper:(Class2*) a andClass3:(Class3*)b
{
NSLog(#"- (int)someFuncWrapper:a andClass3:b <= a=%#, ab=%#", a, b);
return [someFunc: a andClass3:b];
}
Is it possible?
I've read of method swizzling, KVO, forward messaging.
My current approach with method swizzling causes infinite recursion:
- (int)funcToSwizzle:(int)a andB:(int)b
{
int r = a+b;
NSLog(#"funcToSwizzle: %d", r);
return r;
}
- (void)doSimpleSwizzling
{
NSLog(#"r1 = %d", [self funcToSwizzle:10 andB:20]);
Class curClass = NSClassFromString(#"HPTracer");
unsigned int methodCount = 0;
Method *methods = class_copyMethodList( curClass, &methodCount);
for (int i=0; i<methodCount; ++i)
{
SEL originalSelector = method_getName(methods[i]);
if ( strcmp("funcToSwizzle:andB:", sel_getName(originalSelector)) == 0 )
{
Method m1 = class_getInstanceMethod(curClass, originalSelector);
id block3 = ^(id self, int a, int b) {
NSLog(#"My block: %d", a*b);
// get current implementation of "funcToSwizzle".
// copy it. store that "IMP"/"void *" etc
return [self funcToSwizzle:a andB:b];
};
IMP imp3 = imp_implementationWithBlock(block3);
method_setImplementation(m1, imp3);
}
}
NSLog(#"r2 = %d", [self funcToSwizzle:10 andB:20]);
}
And I'm afraid it's impossible to generate a block3 or some method in runtime. There's NSSelectorFromString but no ImplementationFromString.
UPD
I looked at DTrace util, it seems very powerful, but doesn't fit my needs.
It requires disabling SIP on Mac OS, and is either impossible on iOS or possible on jailbreaked device.
What I need from methods interceptions is creating a stable custom "framework" for both Debug and production build modes.
Assume the following class hierarchy. Class A is publicly declared:
#interface A : NSObject
+ (A)createInstance;
- (void)a;
#end
Class _B is a private subclass of A:
#interface _B : A
- (void)a;
- (void)b;
#end
Assume objects of class A should only be created using the factory method createInstance, which creates and returns an instance of _B.
I want to enhance the functionality of an instance of A on a per-instance basis. So I decided to do some ISA swizzling to achieve:
#interface ExtA : A
- (void)a;
#end
#implementation ExtA
- (void)a
{
NSLog("ExtA_a");
[super a];
}
#end
And I do the ISA swizzling using the following method on an NSObject category (naive implementation shown here):
- (void)changeToSubclass:(Class)cls prefix:(NSString*)prefix suffix:(NSString*)suffix
{
NSString* className = [NSString stringWithFormat:#"%#%#%#", prefix ? prefix : #"", NSStringFromClass(object_getClass(self)), suffix ? suffix : #""];
if([className isEqualToString:NSStringFromClass(object_getClass(self))])
{
className = [NSString stringWithFormat:#"%#(%#)", NSStringFromClass(object_getClass(self)), NSStringFromClass(cls)];
}
Class newSubclass = objc_getClass(className.UTF8String);
if(newSubclass == nil)
{
newSubclass = objc_allocateClassPair(object_getClass(self), className.UTF8String, 0);
objc_registerClassPair(newSubclass);
unsigned int listCount = 0;
Method *list = class_copyMethodList(cls, &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(newSubclass, method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
listCount = 0;
list = class_copyMethodList(objc_getMetaClass(class_getName(cls)), &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(objc_getMetaClass(class_getName(newSubclass)), method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
}
object_setClass(self, newSubclass);
}
Everything seemingly works, but I noticed that [super a]; does not behave as expected, actually the implementation of -[A a] is called, if if the superclass in runtime is actually _B.
Replacing the call to super with the following code works, but is ugly, and requires knowledge of and work by developers:
struct objc_super superInfo = {
self,
[self superclass]
};
objc_msgSendSuper(&superInfo, #selector(a));
What does the compiler emit when calling super and any way to change this emitted code?
The difference is minor, but important. The compiler is issuing a function call, not to objc_msgSendSuper, but to objc_msgSendSuper2.
What's the difference, you may ask? It's minor, but important.
From apple's open source:
/********************************************************************
*
* id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...);
*
* struct objc_super {
* id receiver;
* Class class;
* };
********************************************************************/
ENTRY _objc_msgSendSuper
MESSENGER_START
// search the cache (objc_super in %a1)
movq class(%a1), %r11 // class = objc_super->class
CacheLookup SUPER // calls IMP on success
/* Snipped code for brevity */
/********************************************************************
* id objc_msgSendSuper2
********************************************************************/
ENTRY _objc_msgSendSuper2
MESSENGER_START
// objc_super->class is superclass of class to search
// search the cache (objc_super in %a1)
movq class(%a1), %r11 // cls = objc_super->class
movq 8(%r11), %r11 // cls = class->superclass
CacheLookup SUPER2 // calls IMP on success
For those reading who are unfamiliar with x86_64 assembly, the important line of code is here:
movq 8(%r11), %r11 // cls = class->superclass
What does this do, you may ask? It's fairly simple - instead of the caller passing the superclass to search, the objc_msgSend implementation does it.
However, this important distinction causes one crucial problem - when doing a super call, it does not invoke [self class]. Instead, it uses the class of the current implementation, which is, of course, ExtA.
Therefore, the only way to 'fix' this is to change the superclass of ExtA at run-time, which should cause your method invoking to perform as expected.
I am trying to create a simple bridge between lua and my 'native' code. Using the following code I am adding an LuaObject class so that it can used from the lua code.
-(instancetype) init
{
if((self = [super init]))
{
// temp for testing script
L = luaL_newstate();
luaL_openlibs(L);
[self registerClazz:[LuaObject class]];
[self pushFunction:getObjectWithName name:#"getObjectWithName"];
}
return self;
}
-(void) pushFunction:(lua_CFunction)function name:(NSString*)name
{
lua_pushcfunction(L, function);
lua_setglobal(L, [name cStringUsingEncoding:NSASCIIStringEncoding]);
}
int getObjectWithName(lua_State *luaState)
{
NSString *name = [NSString stringWithUTF8String:lua_tostring(luaState, 1)];
lua_pop(luaState, 1);
LuaObject *luaObject = [objectMap objectForKey:name]
void *luaUserdataPtr = lua_newuserdata(luaState, sizeof(LuaObject*));
void *luaObjectPtr = (__bridge_retained void *)luaObjectPtr;
memcpy(ptr, &luaObjectPtr, sizeof(LuaObject*));
luaL_getmetatable(luaState, "LuaObject");
lua_setmetatable(luaState, -2);
return 1;
}
-(void) registerClazz:(Class)clazz
{
luaL_Reg methods[] = {
{ "talk", &proxyLuaObjectCall },
{ "say", &proxyLuaObjectCall },
{ NULL, NULL }
};
luaL_newmetatable(L, "LuaObject");
luaL_newlib(L, methods);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
}
int proxyLuaObjectCall(lua_State *luaState, void* caller)
{
luaL_checkudata(luaState, 0, "LuaObject");
}
Now when proxyLuaObjectCall is called from lua, I want to be able to retrieve the instance of the LuaObject on which the method is being called. Above code works perfectly when calling a method without any arguments. But when calling a method that has any arguments the code fails with the error
bad argument #0 to '' (LuaObject expected, got table).
For example when using the following code in lua:
This works perfectly:
myObject = getObjectWithName("nominator");
myObject.talk();
This fails miserably
myObject = getObjectWithName("nominator");
myObject.say("And the winner is");
// Result: bad argument #0 to 'say' (LuaObject expected, got table).
--
myObject = getObjectWithName("nominator");
myObject.say("And the winner is", "Joan");
// Result: bad argument #0 to 'say' (LuaObject expected, got string).
I have tried changing the index when calling a method that has arguments but nothing on the stack contains a reference to the LuaObject instance.
What am I doing wrong here? What should I do to retrieve the instance of the LuaObject on which the method is called?
Everything looks good to me, but you probably should be calling it using the method notation:
myObject = getObjectWithName("nominator");
myObject:say("And the winner is");
myObject:say("") is the same as myObject.say(myObject, ""), which is what your API seems to expect.
On the other hand, #siffiejoe may be right as according to this PiL example, it should probably be luaL_checkudata(luaState, 1, "LuaObject"). In fact, you may need both of these changes to make it work.
I am writing an iOS library to embed Lua in games and have encountered a problem regarding userdata. I want users to be able to treat my library objects as normal tables (in Lua scripts) to set attributes and for these attributes to be available to the base objects in the library. For instances a user script may have
line = display.newLine
line.width = 3
Then the width field should be accessible from within the library (Objective C/C) code.
I have this working, sort of, but after running for a few seconds I get an EXC_BAD_ACCESS error, so obviously I am accessing a freed object or have some other type of memory corruption, but I can't seem to figure out why.
I have trimmed down my code to just one example to reproduce the error. First I have a base Objective C object that implements the library functionality. The header is shown below:
#import "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#interface GeminiLine : NSObject {
int selfRef;
int propertyTableRef;
lua_State *L;
}
#property (nonatomic) int propertyTableRef;
-(id)initWithLuaState:(lua_State *)luaStat;
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt;
#end
This class keeps a reference to the lua_State object and integer Lua references to it's corresponding Lua userdata and uservalue (the tables associated with the userdata). The reference propertyTableRef is used to access the object attributes (uservalue table).
The implementation is given below:
#import "GeminiLine.h"
#implementation GeminiLine
#synthesize propertyTableRef;
-(id)initWithLuaState:(lua_State *)luaState {
self = [super init];
if (self) {
L = luaState;
}
return self;
}
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt {
lua_rawgeti(L, LUA_REGISTRYINDEX, propertyTableRef);
//lua_pushstring(L, key);
//lua_gettable(L, -2);
lua_getfield(L, -1, key);
if (lua_isnil(L, -1)) {
return dflt;
}
return lua_tonumber(L, -1);
}
-(void)dealloc {
luaL_unref(L, LUA_REGISTRYINDEX, propertyTableRef);
[super dealloc];
}
#end
The only non-lifecycle method here is the getDoubleForKey method, which accesses the Lua uservalue associated with the userdata for the object.
The C code to bind the object to Lua is given here:
///////////// lines ///////////////////////////
static int newLine(lua_State *L){
NSLog(#"Creating new line...");
GeminiLine *line = [[GeminiLine alloc] initWithLuaState:L];
GeminiLine **lLine = (GeminiLine **)lua_newuserdata(L, sizeof(GeminiLine *));
*lLine = line;
[Gemini shared].line = line;
luaL_getmetatable(L, GEMINI_LINE_LUA_KEY);
lua_setmetatable(L, -2);
// append a lua table to this user data to allow the user to store values in it
lua_newtable(L);
lua_pushvalue(L, -1); // make a copy of the table becaue the next line pops the top value
// store a reference to this table so we can access it later
line.propertyTableRef = luaL_ref(L, LUA_REGISTRYINDEX);
// set the table as the user value for the Lua object
lua_setuservalue(L, -2);
NSLog(#"New line created.");
return 1;
}
static int lineGC (lua_State *L){
NSLog(#"lineGC called");
GeminiLine **line = (GeminiLine **)luaL_checkudata(L, 1, GEMINI_LINE_LUA_KEY);
[*line release];
return 0;
}
static int lineIndex( lua_State* L )
{
NSLog(#"Calling lineIndex()");
/* object, key */
/* first check the environment */
lua_getuservalue( L, -2 );
if(lua_isnil(L,-1)){
NSLog(#"user value for user data is nil");
}
lua_pushvalue( L, -2 );
lua_rawget( L, -2 );
if( lua_isnoneornil( L, -1 ) == 0 )
{
return 1;
}
lua_pop( L, 2 );
/* second check the metatable */
lua_getmetatable( L, -2 );
lua_pushvalue( L, -2 );
lua_rawget( L, -2 );
/* nil or otherwise, we return 1 here */
return 1;
}
// this function gets called with the table on the bottom of the stack, the index to assign to next,
// and the value to be assigned on top
static int lineNewIndex( lua_State* L )
{
NSLog(#"Calling lineNewIndex()");
int top = lua_gettop(L);
NSLog(#"stack has %d values", top);
lua_getuservalue( L, -3 );
/* object, key, value */
lua_pushvalue(L, -3);
lua_pushvalue(L,-3);
lua_rawset( L, -3 );
NSLog(#"Finished lineNewIndex()");
return 0;
}
// the mappings for the library functions
static const struct luaL_Reg displayLib_f [] = {
{"newLine", newLine},
{NULL, NULL}
};
// mappings for the line methods
static const struct luaL_Reg line_m [] = {
{"__gc", lineGC},
{"__index", lineIndex},
{"__newindex", lineNewIndex},
{NULL, NULL}
};
int luaopen_display_lib (lua_State *L){
// create meta tables for our various types /////////
// lines
luaL_newmetatable(L, GEMINI_LINE_LUA_KEY);
lua_pushvalue(L, -1);
luaL_setfuncs(L, line_m, 0);
/////// finished with metatables ///////////
// create the table for this library and popuplate it with our functions
luaL_newlib(L, displayLib_f);
return 1;
}
The key methods here are newLine and lineNewIndex. In newLine I create the objective C GeminiLine object corresponding to the Lua object and store a pointer to it in Lua userdata. I also store a pointer to the new object in a singleton Gemini object, which is the object that provides the main program with access to executing Lua scripts. This class is given here:
Gemini *singleton = nil;
#interface Gemini () {
#private
lua_State *L;
}
#end
#implementation Gemini
#synthesize line;
- (id)init
{
self = [super init];
if (self) {
L = luaL_newstate();
luaL_openlibs(L);
}
return self;
}
+(Gemini *)shared {
if (singleton == nil) {
singleton = [[Gemini alloc] init];
}
return singleton;
}
-(void)execute:(NSString *)filename {
int err;
lua_settop(L, 0);
NSString *luaFilePath = [[NSBundle mainBundle] pathForResource:filename ofType:#"lua"];
setLuaPath(L, [luaFilePath stringByDeletingLastPathComponent]);
err = luaL_loadfile(L, [luaFilePath cStringUsingEncoding:[NSString defaultCStringEncoding]]);
if (0 != err) {
luaL_error(L, "cannot compile lua file: %s",
lua_tostring(L, -1));
return;
}
err = lua_pcall(L, 0, 0, 0);
if (0 != err) {
luaL_error(L, "cannot run lua file: %s",
lua_tostring(L, -1));
return;
}
}
#end
For my test program I have created an application using the single view template. I modified the applicationDidFinishLaunching method on the AppDelegate to call a test script as follows:
-(void) update {
double width = [[Gemini shared].line getDoubleForKey:"width" withDefault:5.0];
NSLog(#"width = %f", width);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
[[Gemini shared] execute:#"test"];
timer = [NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(update)
userInfo:nil
repeats:YES];
....
I have also included a timer that fires 100 times per second and an update method as its target. The update method retrieves the attribute set in the lua script, width, and logs it with NSLog.
The test.lua script I am using is given below:
display = require('display')
line = display.newLine()
line.width = 3;
Now when I run this code, it executes correctly for several seconds, printing out the correct message and appropriate line width, but then it fails with an EXC_BAD_ACCESS error on the NSLog(#"width = %f", width); line of the update method. At first I thought maybe the line object was being garbage collected, but the lineGC method would log this and it does not. So I'm convinced the problem is in the way I am using the uservalue of my Lua userdata, either in setup or access.
Can anyone see an error in the way I have implemented this?
EDIT
To confirm that my userdata isn't being garbage collected, I disabled the GC before even loading the script using lua_gc(L, LUA_GCSTOP, 0);. Still get exactly the same problem.
I forgot to mention earlier that I'm using Lua 5.2.
Turning on every memory debugging flag using "Edit scheme" indicates that the error is happening in the following Lua code base function on the call to setsvalue2s, which is actually a macro:
LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
StkId t;
lua_lock(L);
t = index2addr(L, idx);
api_checkvalidindex(L, t);
setsvalue2s(L, L->top, luaS_new(L, k));
api_incr_top(L);
luaV_gettable(L, t, L->top - 1, L->top - 1);
lua_unlock(L);
}
I've run into similar issues. My guess, is that the Lua memory manager (or ObjC manager) is releasing the object. It works correctly for a few seconds because it will not have been garbage collected.
I'm pretty sure I have the answer to my own question now. The problem is in the getDoubleForKey method:
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt {
lua_rawgeti(L, LUA_REGISTRYINDEX, propertyTableRef);
//lua_pushstring(L, key);
//lua_gettable(L, -2);
lua_getfield(L, -1, key);
if (lua_isnil(L, -1)) {
return dflt;
}
return lua_tonumber(L, -1);
}
I'm new to Lua and had not realized that I needed to empty the stack after making calls like this. When my library functions are invoked by Lua there is no need, but here I am making the call so Lua does not bail me out.
I found this out by printing out the stack size at the top of the method and seeing it increasing with every call. Eventually the stack got so big that bad things happened. The solution is to empty out the stack before exiting the method:
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt {
lua_rawgeti(L, LUA_REGISTRYINDEX, propertyTableRef);
lua_getfield(L, -1, key);
if (lua_isnil(L, -1)) {
lua_pop(L,2);
return dflt;
}
double rval = lua_tonumber(L, -1);
lua_pop(L, 2);
return rval;
}
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...
}