How to pass unnamed struct in Objective-C protocol method? - objective-c

Given the example objective C protocol below:
#protocol testp
- (void)testMethod:(struct {int a;})arg1 reply:(void (^)(NSError *))arg2;
#end
Assuming I've already setup the protocol connection. How can I call the method? The issue is the struct type, I'm familiar with passing basic arguments like int or NSString, however I'm unsure how to handle the unnamed struct in the protocol.
If I use:
struct { int a; } test = {1};
[p testMethod:test reply:^(NSError* error ){
NSLog(#"error: %#", error);
}];
I get the following error:
error: sending 'struct (unnamed struct at .. to parameter of incompatible type 'struct (unnamed struct at ..

The anonymous struct declared in the method signature..
- (void)testMethod:(struct { int a; })arg1 reply:(void (^)(NSError *))arg2;
..and in front of the invocation..
struct { int a; } test = { 1 };
..are two different structures, which are not compatible. For anonymous structures one option you may employ is to pass a pointer to a struct, like this:
- (void)method:(struct { int a; } *)str reply:(void (^)(NSError *))arg2;
...
struct { int a; } val = { 1 };
[obj method:&val reply:^(NSError *error) {
NSLog(#"error: %#", error);
}];
It should compile, probably with warning about incompatible pointers, but will work just fine as long as the memory layout matches. This approach is very fragile and unreliable, however, and I can't see any reason to not use a named structure:
typedef struct {
int val;
} TDWStruct;
#protocol TDWReplyable <NSObject>
- (void)method:(TDWStruct)str reply:(void (^)(NSError *))arg2;
#end
...
TDWStruct val = { 1 };
[obj method:val reply:^(NSError *error) {
NSLog(#"error: %#", error);
}];

Related

Block mutates a variable that is indirectly in it's scope

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;
};

How to know if Objective C pointer is of `Class` type?

I would like to know if a pointer in my program is pointing to a Class type.
Something like:
if ([anObject isKindOfClass:[Class class]]])
This an error because [Class class] does not exist.
The closet I can come to is this:
NSLog(#"Will run");
const char *nameOfClass = class_getName(#"DoesNotExist");
if (nameOfClass == NULL || (strlen(nameOfClass) == 0)
{
NSLog(#"Not a class");
} else {
NSLog(#"String: %s", nameOfClass);
}
NSLog(#"Did run");
Where an empty const char *nameOfClass would tell me that it isn't in fact a Class object. Any other ideas?
There's an Obj-C runtime function called object_isClass.
#import <objc/runtime.h>
if (object_isClass(anObject))
Another valid approach would be to use class_isMetaClass(object_getClass(anObject)), since the class of a class is a metaclass.

Modify object type at runtime

Is it possible in Objective C to modify an object type at runtime without the compiler complaining?
e.g.
id object;
in an init method
initWithType:(someEnumType) type
then depending on type set the object to a class type.
How is this done without the compiler flagging errors that object does not declare someMethod?
The most common way to do something like that is in a factory method, rather than an initializer:
typedef enum {
etString,
etNumber
} EnumType;
#implementation MyFactory
+(id)makeNewObjectWithType:(EnumType)et {
id res;
switch (et) {
case etString:
res = [NSString string];
break;
case etNumber:
res = [NSNumber numberWithInt:12345];
break;
default:
res = nil;
break;
}
return res;
}
#end

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...
}

Multiple methods warning

I'm currently learning Objective C and in the process I've made the silly little program below. The program compiles fine - however I get the warning "multiple methods named '-setName:' found".
I've only interfaced and implemented the method once.
What does this warning mean, and how do I correct it?
#import <Foundation/Foundation.h>
// these are the three yoga-exercises we can perform
typedef enum {
kCobra,
kUniversal,
kDog
} ExerciseName;
// translating our variables into human
NSString *nameExercise (ExerciseName nameExercise)
{
switch (nameExercise) {
case kCobra:
return #"Cobra Pose";
break;
case kUniversal:
return #"Universal Stretch";
break;
case kDog:
return #"Dog Pose";
break;
}
return #"no clue!";
} // nameExercise
#interface Exercise : NSObject
{
ExerciseName name;
}
-(void) setName: (ExerciseName) name;
-(void) exerciseDo;
#end
#implementation Exercise
-(void) setName: (ExerciseName) n {
name = n;
} // setName
-(void) exerciseDo {
NSLog(#"Exercise: %#",
nameExercise(name));
}
#end
void executeExercises(id exercises[], int count) {
int i;
for(i=0; i<count; i++) {
id exercise = exercises[i];
[exercise exerciseDo];
}
}
int main (int argc, const char * argv[]) {
id exercises[1];
exercises[0] = [Exercise new]; // initiating an object of class Exercise
[exercises[0] setName:kDog];
executeExercises(exercises, 1);
return 0;
} //main
the meaning of the message is that there are multiple selectors with the name setName: in the translation (that is, it is declared in at least on other place among all included headers). the compiler may choose the wrong selector (which can introduce undefined behavior).
you can typically correct the problem using one (or more) of the following approaches:
1) rename the method to a unique name: e.g. setExerciseName may be ok, if not used in other translations.
2) match the signature of the other selector. e.g. setName:(NSString *)name
3) use type safety:
Exercise * ex = [Exercise new];
[ex setName:kCobra];
4) cast the variable to the type: [(Exercise*)exercise setName:kCobra];
5) restore the type with a new variable: Exercise * ex = exercise;
since you have declared the var as an id, you have erased the type, and it means that the object may respond any visible selector. in general, you should not erase the type in this manner, except when truly necessary.
the best approach i see is a combination of 1 and 3:
[ex setExerciseName:kCobra];