Two questions:
Is it possible to send objective-c message to C void * from C function?
Is it possible to hint void * with <SomeProtocol> in C-function declaration? In function body?
(pseudocode)
// myfunc.h
void myfunc(void *object, int param);
// myfunc.c
void myfunc(void *object, int param) {
// desired (pseudocode):
// [<SomeProtocol>(id)object method:param];
}
// objective-c controller
# include "myfunc.h"
// ....
#implementation
- (void)visible_to_outer_world {
Object *o = [Object new];
myfunc(o, 5);
}
// ....
#end
Is it possible to send an Objective-C message to a void * from a C function?
Not sure why you would want it, but if you're compiling as Objective-C:
void bar(void *ptr)
{
// MRC version:
[(id)ptr someMessage];
// ARC (alias "ugly") version:
[(__bridge id)ptr someMessage];
}
Foo *foo = [[Foo alloc] init];
bar(foo);
Is it possible to hint void * with <SomeProtocol> in a C function declaration? In a function body?
No.
Related
This is the most minimal example I could think of:
#import <Foundation/Foundation.h>
#interface ObjCClass:NSObject
#property void (^aBlockInner) ();
-(void)method;
-initWithBlock:(void (^)())aBlock;
#end
#implementation ObjCClass
- (void)method{
self->_aBlockInner();
}
-(instancetype)initWithBlock:(void (^)())aBlock
{
self->_aBlockInner = aBlock;
return self;
}
#end
struct cppClass{
cppClass(){
objCClassInstance = [[ObjCClass alloc] initWithBlock:^{
modifyY();
}];
[objCClassInstance method];
}
void modifyY() {
y++;
}
int y = 0;
ObjCClass* objCClassInstance;
};
int main()
{
cppClass a{};
NSLog(#"Y is:%d",a.y);
}
The member variable y is supposed to stay untouched as blocks are supposed to copy their “captures”. Though, the final print outputs:
Y is:1
Have I misunderstood Objective-C blocks?
To compile on a macOS do: clang++ main.mm -framework Foundation
Much more minimal example would be as follows:
struct MClass {
int i{};
MClass() {
void(^block)() = ^{
++i;
};
block();
}
};
int main() {
MClass var;
NSLog(#"%d", var.i);
}
This snippet has exactly the same "problem", and i member variable gets changed within a block. This happens, because when you refer to member variables from inside member functions, this implicitly adds this pointer to the expression, so it's not actually i member variable that gets captured, but this pointer which encompasses it. The block above can be equivalently rewritten like this:
void(^block)() = ^{
++this->i;
};
The block makes a copy of the pointer to the same object, not object itself. However, if you had the owning object referred to by value, and not a pointer, you would not be able in fact, alter the object, because the copies a block makes are constant:
struct MClass {
int i{};
};
int main() {
MClass var;
void(^block)() = ^{
// Compile-time error: Read-only variable is not assignable
++var.i;
};
block();
NSLog(#"%d", var.i);
}
And this can only be done with use of the __block modifier:
__block MClass var;
void(^block)() = ^{
++var.i;
};
And note that I can not pass in a ViewController pointer due to this function being passed into another function.
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
NSString *str = #"";
int i;
for(i=0; i<argc; i++)
{
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
str = [NSString stringWithFormat:#"%#\n%s = %s\n", str, azColName[i], argv[i] ? argv[i] : "NULL"];
}
printf("\n");
//tvDisplay is a UITextView
[tvDisplay setText:str]; // <---- ??? how to get to an iVar
return 0;
}
the call:
rc = sqlite3_exec(db, pSQL[i], callback, 0, &zErrMsg);
Callback functions typically have an argument that allows you to pass along arbitrary data (it's usually a void * called context or something similar). You can pass in the object that you need to access when you set up the callback function, and then retrieve it within the callback function:
static void myCallback(int someResult, void *context) {
SomeClass *someObject = (SomeClass *)context;
[someObject doStuff];
}
In your particular case, the place for the "arbitrary data that you want to access in the callback function" is the void * argument right after the callback function itself that you have presently set to 0:
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
Keep in mind that you're responsible for ensuring that any data you stick in there remains valid while the callback has not yet returned, and, if necessary, free it in the callback.
Have a simple one-off tasks which needs a progress bar. OpenSSL has a useful
callback which one can use for that:
rsa=RSA_generate_key(bits,RSA_F4,progressCallback,NULL);
with
static void callback(int p, int n, void *arg) {
.. stuff
However I want to call this from ObjectiveC without too much ado:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.labelText = #"Generating CSR";
[self genReq:^(int p,int n,void *arg) {
hud.progress = --heuristic to guess where we are --
} completionCallback:^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}];
With Genrec: as an objC method:
-(void)genReq:(void (^)(int,int,void *arg))progressCallback
completionCallback:(void (^)())completionCallback
{
.....
rsa=RSA_generate_key(bits,RSA_F4,progressCallback,NULL);
assert(EVP_PKEY_assign_RSA(pkey,rsa));
rsa=NULL;
....
completionCallback();
}
Now completionCallback(); works splendidly and as expected. But I get a compiler warning/error which I cannot quell for the progress callback:
Passing 'void (^__strong)(int, int, void *)' to parameter of incompatible type 'void (*)(int, int, void *)'
So am curious -- what is the appropriate way to do this ?
Thanks,
Dw.
All code is just typed into this answer, test carefully before using!
Function pointers and blocks are not the same thing; the former is just a reference to code, the latter is a closure containing both code and an environment; they are not trivially interchangeable.
You can of course use function pointers in Objective-C, so that is your first option.
If you wish to use blocks then you need to find a way to wrap a block and pass it as a function reference...
The definition of RSA_generate_key is:
RSA *RSA_generate_key(int num,
unsigned long e,
void (*callback)(int,int,void *),
void *cb_arg);
The fourth argument can be anything and is passed as the third argument to the callback; this suggests we could pass the block along with a pointer to a C function which calls it:
typedef void (^BlockCallback)(int,int);
static void callback(int p, int n, void *anon)
{
BlockCallback theBlock = (BlockCallback)anon; // cast the void * back to a block
theBlock(p, n); // and call the block
}
- (void) genReq:(BlockCallback)progressCallback
completionCallback:(void (^)())completionCallback
{
.....
// pass the C wrapper as the function pointer and the block as the callback argument
rsa = RSA_generate_key(bits, RSA_F4, callback, (void *)progressCallback);
assert(EVP_PKEY_assign_RSA(pkey,rsa));
rsa = NULL;
....
completionCallback();
}
And to invoke:
[self genReq:^(int p, int n)
{
hud.progress = --heuristic to guess where we are --
}
completionCallback:^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
];
Whether you need any bridge casts (for ARC) is left as an exercise!
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...
}
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.