Objective C adding methods to classes argument mismatch - objective-c

I am working on this piece of code, basically adding a block to NSObject:
class_addMethod(object_getClass([NSObject class]), #selector(toUpper2:), imp_implementationWithBlock(^NSString*(id self, SEL _cmd, NSString* s) {
NSLog(#"self: %#", self);
NSLog(#"_cmd: %#", _cmd); // I know %# is not SEL, but look for yourself
NSLog(#"s: %#", s);
return [s uppercaseString];
}), "##:#"); // the type signature was created using #encode
To me, this looks fairly innocent, but if I do this: (I've defined a +toUpper2 in a different class too, so that the compiler does not complain):
[(id)[NSObject class] toUpper2:#"hallo"];
This happens:
2018-07-06 16:45:52.302676+0200 xctest[43736:32056962] self: NSObject
2018-07-06 16:45:52.302706+0200 xctest[43736:32056962] _cmd: hallo
2018-07-06 16:45:52.302721+0200 xctest[43736:32056962] s: (null)
As you can see, the arguments got messed up. What's more, if i enact the same method using performSelector, like that:
[NSObject performSelector:#selector(toUpper2:) withObject:#"hallo"];
Then, things get even more astray:
2018-07-06 16:45:52.302737+0200 xctest[43736:32056962] self: NSObject
2018-07-06 16:45:52.302751+0200 xctest[43736:32056962] _cmd: hallo
2018-07-06 16:45:52.302763+0200 xctest[43736:32056962] s: hallo
Can anyone explain this behaviour?
Best regards,
thejack

The docs are wrong, bug filed (41908695). The objc header file is correct:
/**
* Creates a pointer to a function that will call the block
* when the method is called.
*
* #param block The block that implements this method. Its signature should
* be: method_return_type ^(id self, method_args...).
* The selector is not available as a parameter to this block.
* The block is copied with \c Block_copy().
*
* #return The IMP that calls this block. Must be disposed of with
* \c imp_removeBlock.
*/
OBJC_EXPORT IMP _Nonnull
imp_implementationWithBlock(id _Nonnull block)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
The reason why it was implemented this way was speed speed speed. And simplicity.
Specifically, a method decomposes into a C function that always takes at least two arguments; self and _cmd, both of which happen to pointers. That is followed by 0...N arbitrary arguments which will be packed into registers or onto the stack arbitrarily as defined by the targeted architectures ABI.
A block call site, on the other hand, always decomposes into a C function call where the function has one guaranteed argument; a reference to the block. It is this reference through which the compiler can emit code to reference captured state, if any. Like a method, the block's args are followed by an arbitrary list of arguments.
Now, re-encoding argument lists on any architecture is a nightmare. Slow, prone to error, and extremely complex.
To avoid that, imp_implementationWithBlock() does some behind the scenes magic that returns a function pointer that, when invoked, treats the first argument like a pointer (should be self) into the second argument's slot (overwrites _cmd), shoves the block reference into the first argument's slot, and then tail calls the block's code.
The block doesn't know that it was invoked as a method. And the objc runtime doesn't know that a method invocation trampolined thru to a block.

I haven't used this method myself, but it looks to me like you've misunderstood the API here.
The docs describe the structure of the block parameter.
block
The block that implements this method. The signature of block should be method_return_type ^(id self, self, method_args …). The selector of the method is not available to block. block is copied with Block_copy().
The emphasis here is mine, but should make it quite clear that your SEL argument is not appropriate here. I'm not entirely sure why they've duplicated self in the description, especially given that you're typically seeing your method_args begin at that parameter index.

Related

Why does ARC only sometimes retain a __block out pointer?

1) Why does this retain its __block var:
{
void (^blockWithOutPointer)(NSObject * __autoreleasing *) = ^(NSObject * __autoreleasing * outPointer) {
*outPointer = [NSObject new];
};
NSObject * __block blockVar1;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(),
^{
NSLog(#"blockVar1: %#",
blockVar1);
// prints non-nil. WHY????
});
blockWithOutPointer(&blockVar1);
}
2) But this doesn't?
void (^blockWithOutPointerThatDispatchesLater)(NSObject * __autoreleasing *,
dispatch_block_t) = ^(NSObject * __autoreleasing * outPointer,
dispatch_block_t block) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(),
block);
*outPointer = [NSObject new];
};
{
NSObject * __block blockVar2;
blockWithOutPointerThatDispatchesLater(&blockVar2,
^{
NSLog(#"blockVar2: %#",
blockVar2);
});
// prints nil, which is expected.
}
3) If I instead use an __autoreleasing variable as my out pointer destination, and then assign that variable to my __block pointer, everything works fine.
{
NSObject * __autoreleasing autoreleasingVar;
NSObject * __block blockVar3;
blockWithOutPointerThatDispatchesLater(&autoreleasingVar,
^{
NSLog(#"blockVar3: %#",
blockVar3);
});
blockVar3 = autoreleasingVar;
// prints non-nil, which is expected.
}
I've read CRD's answer about ARC pointer-to-pointer issues, it makes sense that #2 would print nil because ARC assumes that blockVar2 is __autoreleasing, and doesn't retain its value. Thus, in #3, when we assign autoreleasingVar to blockVar3, ARC properly retains the value. However, there is no such assignment for #1. Why does #1 retain its value?
Even more amazingly, #1 is unaffected if I wrap the out-pointer assignment in an #autoreleasepool:
{
void (^blockWithOutPointer)(NSObject * __autoreleasing *) = ^(NSObject * __autoreleasing * outPointer) {
#autoreleasepool {
*outPointer = [NSObject new];
}
};
NSObject * __block blockVar1;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(),
^{
NSLog(#"blockVar1: %#",
blockVar1);
// still prints non-nil. WHY???
});
blockWithOutPointer(&blockVar1);
}
Whereas #3 crashes, as expected since the #autoreleasepool released the out-pointer's object, and I guess ARC doesn't set __autoreleasing variables to nil.
void (^blockWithOutPointerThatDispatchesLater)(NSObject * __autoreleasing *,
dispatch_block_t) = ^(NSObject * __autoreleasing * outPointer,
dispatch_block_t block) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(),
block);
#autoreleasepool {
*outPointer = [NSObject new];
}
};
{
NSObject * __autoreleasing autoreleasingVar;
NSObject * __block blockVar3;
blockWithOutPointerThatDispatchesLater(&autoreleasingVar,
^{
NSLog(#"blockVar3: %#",
blockVar3);
// crashes on the NSLog!
});
blockVar3 = autoreleasingVar;
}
I filed a radar about this.
You have discovered a "feature" of the block implementation and (at least for cases 1 & 2, I haven't checked further) it is nothing to do with __autoreleasing per se.
First let's look at your case 1. You seem surprised this prints a non-nil value. It works exactly as expected:
blockWithOutPointer(&blockVar1); executes, assigning a value to blockVar1; then
the block ^{ NSLog(#"blockVar1: %#", blockVar1); } is executed by GCD and the stored by (1) is printed.
(Note: You can removed the __autoreleasing qualifiers and it will work the same as this is the inferred mode for pass-by-writeback parameters.)
Now your case 2. This is where you hit the "feature":
In Objective-C objects are heap allocated; and
Objective-C blocks are objects; so
Ergo blocks are heap allocated...
Except when they are not...
As an optimisation the block specification allows blocks and their captured __block variables to be stack allocated and only moved onto the heap when their lifetime needs to be longer than the stack frame are in.
As an optimisation it should (a) be essentially invisible to the programmer - apart from any perf benefit and (b) not change the semantics in anyway. However Apple decided to initially introduce it as a "programmer assisted optimisation" and have then slowly improved matters.
The behaviour of your case 2 is all down to when the block and __block variable get copied onto the heap. Let's look at the code:
NSObject * __block blockVar2;
This declares blockVar2 to have __block storage duration, which allows a block to alter the value of this locally declared variable. At this point the compiler does not know whether a block may access it, in which case blockVar2 will need to be on the heap, or whether it won't, in which case it may be on the stack.
The compiler decides it prefers the stack and allocates blockVar2 there.
blockWithOutPointerThatDispatchesLater(&blockVar2,
Now the compiler needs to pass the address of blockVar2 as the first argument, the variable is currently on the stack, and the compiler emits code to calculate its address. That address is on the stack.
^{
NSLog(#"blockVar2: %#",
blockVar2);
});
Now the compiler gets to the second argument. It sees the block, that the block accesses blockVar2, and that blockVar2 is qualified by __block so it must capture the variable itself and not the variables value.
The compiler decides the block should go on the heap. For this to work it needs to migrate blockVar2 onto the heap, so it does that along with its current value nil...
Oops!
The first argument is the address of the original blockVar2 on the stack, while the second argument is a block which in turn references the cloned blockVar2 on the heap.
When the code is executed blockWithOutPointerThatDispatchesLater() allocates an object and stores its address in the stack blockVar2; then GCD executes the delayed block which prints of the value of the heap blockVar2, which is nil.
A “Fix”
Just change your code to:
NSObject * __block blockVar2;
dispatch_block_t afterBlock = ^{
NSLog(#"blockVar2: %#", blockVar2);
};
blockWithOutPointerThatDispatchesLater(&blockVar2, afterBlock);
i.e. pre-calculate the second argument expression. Now the compiler sees the block before it sees &blockVar2, moves the block and blockVar2 to the heap, and the value generated for &blockVar2 is the address of the heap version of blockVar2.
Expanded Conclusion: Bug or Feature?
The original answer simply stated this is clearly a bug, not a feature, and suggest you file a bug report, noting it's been reported before but another report won't harm.
However that might be a little unfair, yes it is a bug, the question is what the bug actually is. Consider:
In (Objective-)C variables, allocated on the stack or statically, and allocated dynamic memory blocks do not move during their lifetime.
Compiler optimisations in general should not alter the meaning or correctness of a program.
Apple has implemented a compiler optimisation – storing blocks and captured __block variables on the stack – in a way which can move __block attributed variables during their lifetime and in doing so the meaning and correctness of a program can be altered.
The result is a simple re-ordering of program statements in a way which should not alter program meaning or correctness in fact does. This is bad!
Given the history of the optimisation, as implemented by Apple, which by-design relied on programmer assistance (though it has since been made more automatic) for its correctness this could simply be seen as another “feature” of the chosen implementation.
Recommendation
Never, ever, apply the address-of (&) operator to a variable with __block storage duration. If may work if you do, but it just as easily may not.
If you need to use a __block variable as a pass-by-writeback argument then copy it to a local temporary first, make the call, and finally copy it back again.
HTH
There's probably a misconception in 1):
If a variable will be imported into a block, which is marked with __block the compiler generates a helper struct containing various fields and the variable corresponding to the given source. That is, there's no pointer blockVar1, instead there's a whole struct instead.
If a block imports this variable and needs to be copied (for example, when asynchronously submitted) it also "moves" this helper struct onto the heap before "moving" the block itself onto the heap.
This statement
NSObject * __block blockVar1;
will initialise the helper struct which embeds the actual variable and initialises it to nil. The corresponding address of the variable points into the stack.
When the compiler parses this statement:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(),
^{
NSLog(#"blockVar1: %#",
blockVar1);
// prints non-nil. WHY????
});
The compiler generates code for the compound statement, specifically it creates a struct representing the block which initially exists on the stack, too. Since this block requires to be moved to the heap before executing dispatch_async, it also moves the helper struct onto the heap in subsequently generated code.
Additionally, since the imported variable is a NSObject pointer, it also assigns function pointers (located in the helper struct) which "keep" and "dispose" the object, which will be called when the helper struct is "moved" to the heap, respectively when it is destroyed.
When you finally execute this statement
blockWithOutPointer(&blockVar1);
the address of the variable has already been changed: the variable is now located on the heap (since the helper struct has been "moved" to the heap") and exists as long as the block.
Now, to the point where you declare (and define) the following block:
void (^blockWithOutPointer)(NSObject * __autoreleasing *)
What causes gripes here is the __autoreleasing modifier, specifically Storage duration of __autoreleasing objects:
That is, __autoreleasing can only be applied to variables with automatic storage duration.
Edit:
This would assume that the given variable is on the stack. However, it is located on the heap. Thus, your program is illformed and the behaviour is undefined.
Regarding the __autoreleasing storage qualifier, the code sample 1) is correct (the _autoreleasing storage qualifier refers to a temporary that is created when passing the variable as a parameter, see comments below).
What I've said previously above should be correct, and what the user is experiencing is thus expected.
For further reference see: http://clang.llvm.org/docs/Block-ABI-Apple.html
However:
There's still a subtle potential issue with a "data race": the statement
blockWithOutPointer(&blockVar1);
will modify a pointer variable which is located on the heap, while executing on the current thread. Later, on the main thread the same variable will be read. Unless current thread equals main thread, this exhibits a classic data race.
While this is not the question and no a complete answer - it shows, how quickly such code becomes more complex than desired and I would recommend to strive for simpler, comprehensible code.
End Edit
I haven't analysed the other code samples
- but at the first glance it seems, using __autoreleasing leads also to an illformed program.

Running a nil block in Objective C [duplicate]

I started using blocks a lot and soon noticed that nil blocks cause bus errors:
typedef void (^SimpleBlock)(void);
SimpleBlock aBlock = nil;
aBlock(); // bus error
This seems to go against the usual behaviour of Objective-C that ignores messages to nil objects:
NSArray *foo = nil;
NSLog(#"%i", [foo count]); // runs fine
Therefore I have to resort to the usual nil check before I use a block:
if (aBlock != nil)
aBlock();
Or use dummy blocks:
aBlock = ^{};
aBlock(); // runs fine
Is there another option? Is there a reason why nil blocks couldn’t be simply a nop?
I'd like to explain this a bit more, with a more complete answer. First let's consider this code:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
void (^block)() = nil;
block();
}
If you run this then you'll see a crash on the block() line that looks something like this (when run on a 32-bit architecture - that's important):
EXC_BAD_ACCESS (code=2, address=0xc)
So, why is that? Well, the 0xc is the most important bit. The crash means that the processor has tried to read the information at memory address 0xc. This is almost definitely an entirely incorrect thing to do. It's unlikely there's anything there. But why did it try to read this memory location? Well, it's due to the way in which a block is actually constructed under the hood.
When a block is defined, the compiler actually creates a structure on the stack, of this form:
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
The block is then a pointer to this structure. The fourth member, invoke, of this structure is the interesting one. It is a function pointer, pointing to the code where the block's implementation is held. So the processor tries to jump to that code when a block is invoked. Notice that if you count the number of bytes in the structure before the invoke member, you'll find that there are 12 in decimal, or C in hexadecimal.
So when a block is invoked, the processor takes the address of the block, adds 12 and tries to load the value held at that memory address. It then tries to jump to that address. But if the block is nil then it'll try to read the address 0xc. This is a duff address, clearly, and so we get the segmentation fault.
Now the reason it must be a crash like this rather than silently failing like an Objective-C message call does is really a design choice. Since the compiler is doing the work of deciding how to invoke the block, it would have to inject nil checking code everywhere a block is invoked. This would increase code size and lead to bad performance. Another option would be to use a trampoline which does the nil checking. However this would also incur performance penalty. Objective-C messages already go through a trampoline since they need to look up the method that will actually be invoked. The runtime allows for lazy injection of methods and changing of method implementations, so it's already going through a trampoline anyway. The extra penalty of doing the nil checking is not significant in this case.
For more information, see my blog posts.
Matt Galloway's answer is perfect! Great read!
I just want to add that there are some ways to make life easier. You could define a macro like this:
#define BLOCK_SAFE_RUN(block, ...) block ? block(__VA_ARGS__) : nil
It can take 0 – n arguments. Example of usage
typedef void (^SimpleBlock)(void);
SimpleBlock simpleNilBlock = nil;
SimpleBlock simpleLogBlock = ^{ NSLog(#"working"); };
BLOCK_SAFE_RUN(simpleNilBlock);
BLOCK_SAFE_RUN(simpleLogBlock);
typedef void (^BlockWithArguments)(BOOL arg1, NSString *arg2);
BlockWithArguments argumentsNilBlock = nil;
BlockWithArguments argumentsLogBlock = ^(BOOL arg1, NSString *arg2) { NSLog(#"%#", arg2); };
BLOCK_SAFE_RUN(argumentsNilBlock, YES, #"ok");
BLOCK_SAFE_RUN(argumentsLogBlock, YES, #"ok");
If you want to get the return value of the block and you are not sure if the block exists or not then you are probably better off just typing:
block ? block() : nil;
This way you can easily define the fallback value. In my example 'nil'.
Caveat: I'm no expert in Blocks.
Blocks are objective-c objects but calling a block is not a message, although you could still try [block retain]ing a nil block or other messages.
Hopefully, that (and the links) helps.
This is my simple nicest solution… Maybe there is possible to write one universal run function with those c var-args but I don’t know how to write that.
void run(void (^block)()) {
if (block)block();
}
void runWith(void (^block)(id), id value) {
if (block)block(value);
}

Double pointer as Objective-C block parameter

Is it possible (and if so, safe) to create/use a block which takes a double pointer as an argument?
For instance:
- (void)methodWithBlock:(void (^)(NSError **error))block;
Additional context, research, and questions:
I'm using ARC.
When I declare the method above and attempt to call it, XCode autocompletes my method invocation as follows: [self methodWithBlock:^(NSError *__autoreleasing *error) {}];
What does __autoreleasing mean here and why is it being added? I presume it has something to do with ARC.
If this is possible and safe, can the pointer still be dereferenced in the block as it would be anywhere else?
In general, what are the important differences between doing what I'm describing, and simply passing a double pointer as a method parameter (e.g. - (void)methodWithDoublePointer:(NSError **)error;)? What special considerations, if any, should be taken into account (again assuming this is possible at all)?
yes, pointers are always just pointers. you only need to make sure you dereference it before sending it a message (assuming objc object).
Also be aware that the pointer may be nil. always check it before trying to dereference it or what have you.
As #verec mentioned if you are using ARC you should declare the parameter as __autoreleasing
according to the docs
__autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return.
remember id is a pointer to an object so that is saying object**
there is no difference between passing pointers to pointers to methods or blocks.
The answers are both Yes & No...
At a base level passing pointers to pointers to blocks is no different than passing them to methods; and, with the usual proviso that your pointers must be valid, is perfectly OK.
However that __autoreleasing is very significant here and is tied up with ARC and pass-by-writeback. Whether using the block will work as expected will be dependent on context as the compiler often uses hidden variables when passing parameters of type NSError * __autoreleasing * as part of the pass-by-writeback implementation.
If pass-by-writeback is not what you need, or is unsuitable, you may wish to declare you block as taking a different type, such as NSError * __strong *. Read this answer which explains what happens under the hood, that should help you decide whether in your context the block declaration is good.
In summary (a) declaring the block is fine, but (b) you need to understand how it may be called and may need to change the signature.
warning: untested
For the sake of argument, let's start with:
typedef NSError * NSErrorPtr ;
- (void) foo: (NSErrorPtr *) errPtr {
*errorPtr = [NSError new] ;
}
errPtr isn't declared either __weak nor __strong.
So, according to ARC, even though its contents is allocated within foo the responsibility for releasing it has to reside somewhere.
Where?
Note that this not a property of double pointers per se. But of your pattern of allocation.
consider:
int ** arrayOfarrayOfInts = {
{1, 2, 3, 4}
, {5, 6, 7, 8}
} ;
- (void) incrementElement: (int **) elem {
++(**elem) ;
}
- (void) bumpFirstColByOne {
for (int i = 0 ; i < 2 ; ++ i) {
int * arrayOfInt = arrayOfarrayOfInts[i] ;
int ** second = &arrayOfInt[0] ;
[self incrementElement: second] ;
}
}
No __autoreleasing needed here because no allocation is taking place ...

Using objc_msgSendSuper to invoke a class method

I was going through and replacing #synthesized(self) locks w/ this method
void _ThreadsafeInit(Class theClassToInit, void *volatile *theVariableItLivesIn, void(^InitBlock)(void))
{
//this is what super does :X
struct objc_super mySuper = {
.receiver = (id)theClassToInit,
.super_class = class_getSuperclass(theClassToInit)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL, NSZone *) = (void *)&objc_msgSendSuper;
// id (*objc_superAllocTyped)(id objc_super, SEL, NSZone *) = (void *)&objc_msgSend;
do {
id temp = [(*objc_superAllocTyped)(&mySuper /*theClassToInit*/, #selector(allocWithZone:), NULL) init];//get superclass in case alloc is blocked in this class;
if(OSAtomicCompareAndSwapPtrBarrier(0x0, temp, theVariableItLivesIn)) { //atomic operation forces synchronization
if( InitBlock != NULL ) {
InitBlock(); //only the thread that succesfully set sharedInstance pointer gets here
}
break;
}
else
{
[temp release]; //any thread that fails to set sharedInstance needs to clean up after itself
}
} while (*theVariableItLivesIn == NULL);
}
which while a bit more verbose exhibits significantly better performance in non-contested cases
along with this little macro (excuse poor formatting, it's very simple). To allow the block to be declared after the initial nil check, looks to help LLVM keep the "already initialized" path extremely fast. That's the only one I care about.
#define ThreadsafeFastInit(theClassToInit, theVariableToStoreItIn, aVoidBlockToRunAfterInit) if( theVariableToStoreItIn == nil) { _ThreadsafeInitWithBlock(theClassToInit, (void *)&theVariableToStoreItIn, aVoidBlockToRunAfterInit); }
So initially implemented it using the commented out sections for objc_superAllocTyped (actually first using [theClassToInit allocWithZone:NULL], which was definitely the best approach :) ), which worked great until I realized that most of the singletons in the project had overridden allocWithZone to return the singleton method... infinite loop. So I figured using objc_msgSendSuper should sort it out quickly, but I get this error.
[51431:17c03] +[DataUtils allocWithZone:]: unrecognized selector sent to class 0x4f9584
The error doesn't seem to be related to the actual problem, as...
(lldb) po 0x4f9584
$1 = 5215620 DataUtils
(lldb) print (BOOL)[$1 respondsToSelector:#selector(allocWithZone:)]
(BOOL) $2 = YES
So I'm definitely missing something... I compared to assembly generated by a [super allocWithZone:NULL] method in an empty class... almost identical except for the functions called have different names (maybe just using different symbols, no idea, can't read it that well).
Any ideas? I can use class_getClassMethod on the superclass and call the IMP directly, but I'm trying to be reasonable in my abuse of the runtime :)
Alright, this wasn't actually that tricky once I recalled that the meta class contains all of the method information for a Class instance obtained via -[self class] or +[self] -> thanks http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html
This error occurred because I was asking the runtime to look up the method in NSObject's set of instance methods, which obviously doesn't contain allocWithZone: . The mistake in the error log presumably originated because the receiver was a metaclass instance, and Apple has their interns implement error logs.
so while with a normal instance method call via objc_msgSendSuper, you would pass a metaclass instance as objc_super.super_class, to invoke a class method, the metaclass itself is needed (everything is one level up).
Example, and a diagram that helped me understand this - (http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html)
struct objc_super mySuper;
mySuper.receiver = theClassToInit; //this is our receiver, no doubt about it
//either grab the super class and get its metaclass
mySuper.super_class = object_getClass( class_getSuperclass( theClassToInit ) );
//or grab the metaclass, and get its super class, this is the exact same object
mySuper.super_class = class_getSuperclass( object_getClass( theClassToInit ) );
Then the message can be resolved correctly. Makes perfect sense now that I started paying attention :P
Anyways, now that I found my mistake I feel like I've leveled up my Objc runtime understanding. I was also able to fix an architectural mistake made two years ago by someone I never met without having to modifying and re-test dozens of classes across 3 projects and 2 static libraries (God I love Objective-C). Replacing the #synchronized construct with a simple function call also halved the compiled code size of those methods. As a bonus, all our singleton accessors are now (more) threadsafe, because the performance cost for doing so is now negligible. Methods which naively re-fetched the singleton object multiple times (or in loops) have seen a huge speedup now that they don't have to acquire and release a mutex multiple times per invocation. All in all I'm very happy it all worked as I'd hoped.
I made a "normal" Objective-C method for this on a category of NSObject, which will work for both instance and Class objects to allow you to invoke a superclass's implementation of a message externally. Warning: This is only for fun, or unit tests, or swizzled methods, or maybe a really cool game.
#implementation NSObject (Convenience)
-(id)performSelector:(SEL)selector asClass:(Class)class
{
struct objc_super mySuper = {
.receiver = self,
.super_class = class_isMetaClass(object_getClass(self)) //check if we are an instance or Class
? object_getClass(class) //if we are a Class, we need to send our metaclass (our Class's Class)
: class //if we are an instance, we need to send our Class (which we already have)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI
return (*objc_superAllocTyped)(&mySuper, selector);
}
so
[self performSelector:#selector(dealloc) asClass:[self superclass]];
would be equivalent to
[super dealloc];
Carry on runtime explorers! Don't let the naysayers drag you into their land of handwaving and black magik boxes, it's hard to make uncompromisingly awesome programs there*.
*Please enjoy the Objective-C runtime responsibly. Consult with your QA team for any bugs lasting more than four hours.

Objective-c symbols ** & +-

Just when I think I'm getting comfortable with Objective-c the mentioned symbols totally throw me down a rabbit hole...
** a double pointer??
& what sort of things can I do with &reference, is a #property? a full on object? weird pointer razzledazzle?
± I see both a + or a - before method declarations; I've seen Java annotate some datatype declarations by manually typing the + and the magic of compiling in Eclipse would change them to a -
I'm likely asking repetitious questions and/or way outta the ballpark on my guesses; thanks for answers/edits.
You're getting into the C portion that objective-c is built on top of.
** is a pointer to a pointer. Since functions in C take arguments by value, it means you can't change the value of the argument in that function. But, by providing a level of indirection and passing a pointer to the pointer, you can change the value.
& means it's a reference. If an argument takes a ** and you have a * variable, pass a reference to it.
Foo *foo;
[self changeFoo: &foo];
- (BOOL)changeFoo: (Foo **)foo
{
// dereference the double pointer and assign a val = alloc init returns a *
*foo = [[Foo alloc] init];
return YES;
}
A common usage in objective-c / cocoa is NSError. It's essentially an out argument.
NSError *err;
BOOL success = [self doSomething:#"Foo" error:&err];
- (BOOL)doSomething:(NSString*)withData error:(NSError**)error
{
}
As you might know, a pointer points to the address of an object and is the way you reference an object. A double pointer is sometimes used in Objective-C, mainly for returning NSErrors, where you want to get back the address, i.e. a pointer, to an error object (NSError) if an error occurred, thus you pass in a pointer assigned to null and the caller can change that pointer so that it points to the address of another pointer which in turn points to an NSError object.
The ampersand (&) is mostly used by the lower level C APIs, e.g. Core Graphics. They are used to reference things, like the current context. As long as most of your code uses square brackets around its method calls you won't see these very often.
Using a + or a - before a method declarations is used to differentiate between class (+) and instance (-) methods. A class methods is called on the class itself (such as alloc), while a instance method is called on an instance of that object (such as init).
- and + before a method declaration designate an instance method and a static class method. To use an instance method you have to create an object of your class before you can call its method, a static method can be called directly from a class type