My colleague and I are building an async data layer heavily based on PromiseKit v1.5.3. We've noticed in certain circumstances, when returning a promise (call it X) from a block passed to then, the next then block actually passes Promse X as the argument to the block, rather than what the former promise actually resolved to. Chaining thenable promises is a pretty important feature for most Promise implementations, so we were pretty surprised.
After some pretty lengthy debug sessions, we've found the problem to be within PromiseKit. During the resolution process, an IsPromise call fails to identify the object as a promise, which is really a simple call to
[result isKindOfClass:[PMKPromise class]]
This call returns nil, and an incorrect branch is executed. Here's the source
The baffling thing about this, is that I don't see any reason for this to happen. I don't think this is a bug in PromiseKit since their code appears to be sound. I've confirmed the underlying object is indeed a PMKPromise since it responds to promise methods such as value and fulfilled. I've even pushed it through the correct branch using the debugger and it executes correctly from there!
Here's an interesting log from some tests while halted at the given line.
Given that isKindOfClass is returning nil, it sounds like that object isn't responding to the message... but it's certainly an NSObject. I'm curious if this may be a weird compiler setting or something. I currently have my optimizations set to none if that's relevant. Has anyone ever seen anything like this or know what's going on? What should I check?
isKindOfClass returns unexpected results when you manage to have the same class twice in your project. So you may have an object of class PMKPromise, but it is a different class (with exactly the same class name, exactly the same behaviour, just a second class). Maybe that's what happens. Obviously setting a breakpoint and checking what the object is would help.
Related
When calling the method
- (void)readPacketsWithCompletionHandler:(void (^)(
NSArray<NSData *> *packets, NSArray<NSNumber *> *protocols))completionHandler;
the completionHandler is either called directly, in case packets are available at call time, or it is called at a later tim when packets become available.
Yet what is nowhere documented is: What happens if I call this method again before the prior set completionHandler has ever been called?
Will the new handler replace the prior set one and the prior set one won't get called at all anymore?
Are both handler scheduled and called as data arrives? And if so, will they be called in the order I passed them, in reverse order, or in random order?
Has anyone any insights on how that method is implemented?
Of course, I can make a demo project, create a test setup, and see what results I get through testing but that is very time consuming and not necessarily reliable. The problem with unspecified behavior is that it may change at will without letting anyone know. This method may behave differently on macOS and iOS, it may behave differently with every new OS release, or depending on the day of the week.
Or does the fact that nothing is documented is by intention? Do I have to interpret that as: You may call this method once and after your callback was executed, you may call it again with the same or a new callback. Everything else is undefined behavior and you cannot and should not rely on any specific behavior if use that API in a different manner.
As nobody has replied so far, I tried my best to figure it out myself. As testing is not good enough for me, here is what I did:
First I extracted the NetworkExtension framework binary from the dyld cache of macOS Big Sur using this utility.
Then I ran otool -Vt over the resulting binary file to get a disassembler dump of the binary.
My assembly skills are a bit rusty but from what I see the completionHandler is stored in a property named packetHandler, replacing any previous stored value there. Also a callback is created in that method and stored on an object obtained by calling the method interface.
When looking at the code of this created callback, it obtains the value of the packetHandler property and sets it to NULL after the value was obtained. Then it creates NSData and NSNumber objects, adds those to NSArray objects and calls the obtained handler with those arrays.
So it seems that calling the method again just replaces the previous completionHandler which is never be called in that case. So you must not rely that a scheduled handler will eventually be called at some time in the future if the tunnel is not teared down if the possibility exists that your code might replace it. Also calling the method multiple times to schedule multiple callbacks has no effect as as only the last one will be kept and eventually be called.
So i've got this code that tries to find an unused upload name, using the user's email and a number at its end. It does this with a list of uploaded objects we've already collected, the user's email.(upload_name), and the
current number that might be open (it is incremented when a match is found).
The list is not sorted, and it's pretty tricky to sort for a few reasons, so I'm having the method read through the list again if it reaches the end and the upload_number has changed.
- (NSString*)findUnusedUploadNameWithPreviousUploads:(NSMutableArray*)objects withBaseUploadName:(NSString*)upload_name {
previous_upload_number = upload_number;
for (NSString *key in objects) {
// the component of the object name before the first / is the upload name.
NSLog([key componentsSeparatedByString:#"/"][1]);
if ([[key componentsSeparatedByString:#"/"][1]
isEqualToString:([NSString stringWithFormat:#"%#_%ld", S3KeyUploadName1, upload_number])]) {
upload_number++;
NSLog([NSString stringWithFormat:#"upload name: %#_%ld", S3KeyUploadName1, upload_number]);
}
NSLog(#"pang");
}
NSLog(#"ping");
if (previous_upload_number == upload_number) {
return [NSString stringWithFormat:#"%#%ld", upload_name, upload_number];
}
return [self findUnusedUploadNameWithPreviousUploads:objects withBaseUploadName:upload_name];
}
The problem is, the program never reads the "ping". it just leaves the method after the first for loop is done.
Edit: No the NSlogs are fine, you can do simple string OR StringWithFormat.
Edit: Don't mind the unnecessary use of recursion, I did this because the simple way was having the same problem and i wanted to see if a different (albeit unnecessarily recursive) way would share that problem. It does.
Edit: I set a breakpoint in the for loop, and I set a break point at the "ping". It does not reach the ping. It completes the for loop and the ditches the whole thing.
Edit: Please try to help me figure out why it's exiting the the method immediately after the for loop. I'm aware this is stylistically meh, and I promise I'll make it shiny and spotless when it works. =]
Edit: to be clear, the method DOES exit. it does so early I know this because the rest of the program following this method (which is not threaded such that it wouldn't have to wait for it) runs AFTER this for loop, consistently.
There are a couple of possible explanations for the described behavior:
The method never exits. For some reason it blocks or performs infinitely somewhere in the loop. Make sure this is not the case by setting a breakpoint after the place where the message is called (i.e. the place to where it should return).
The method, or some method it calls, throws an exception. While seldom and unsupported in productive Cocoa code it could be some misbehaving 3rd party library or just a simple programmer error, where Cocoa actually does throw. Make sure this does not happen by setting an exception breakpoint in Xcode.
Undefined behavior. This is, sadly, part of official C and heavily exploited by modern compilers. It basically means: Anything can happen, if there's something in your code, where the standard says that the behavior is not defined. One example would be accessing a deallocated object. Another fine reason of undefined behavior can be threads accessing common data in an unsynchronized way.
Other than exceptions there's no explanation for a method to "exit early". If you still can't find the reason I suggest you invest some time to learn the debugger. Single stepping, like Aris suggested, might be a way to find out what's going on.
The question here is more of an educational one. I began to think of this an hour ago while
flipping around a lego block (silly, I know).
A block is an object created on stack, from what I understand.
Let say A is an object, which means we can do:
[A message];
Based on that, if a block is an object, we could also do:
[block message];
Am I correct?
And when the runtime sees that, it would call:
objc_msgSend(block, #selector(message), nil);
So my question is, how can we send a block a message?
And if that is possible, I would imagine that it would also be possible to send a block a message with the arguments being blocks as well?
And, if we could call a block by doing:
block();
Does that mean we could even make a block as a message (SEL) as well, because blocks have the signature void (^)(void) which resembles that of a method?
Because if it would be possible, then the following would really surprise me:
objc_msgSend(block, #selector(block), block);
or:
objc_msgSend(block1, #selector(block2), block3);
I hope my imagination is not running a bit wild and that my understanding is not off here (correct me, if it is).
Blocks are objects only for the purposes of storage and referencing. By making them objects, blocks can be retain/release'd and can, therefore, be shoved into arrays or other collection classes. They also respond to copy.
That's about it. Even that a block starts on the stack is largely a compiler implementation detail.
When a block's code is invoked, that is not done through objc_msgSend(). If you were to read the source for the block runtime and the llvm compiler, then you'd find that:
a block is really a C structure that contains a description of the data that has been captured (so it can be cleaned up) and a pointer to a function -- to a chunk of code -- that is the executable portion of the block
the block function is a standard C function where the first argument must always be a reference to the block. The rest of the argument list is arbitrary and works just like any old C function or Objective-C method.
So, your manual calls to objc_msgSend() treat the block like any other random ObjC object and, thus, won't call the code in the block, nor, if it did (and there is SPI that can do this from a method... but, don't use it) could it pass an argument list that was fully controllable.
One aside, though of relevance.
imp_implementationWithBlock() takes a block reference and returns an IMP that can be plugged into an Objective-C class (see class_addMethod()) such that when the method is invoked, the block is called.
The implementation of imp_implementationWithBlock() takes advantage of the layout of the call site of an objective-c method vs. a block. A block is always:
blockFunc(blockRef, ...)
And an ObjC method is always:
methodFunc(selfRef, SEL, ...)
Because we want the imp_implementationWithBlock() block to always take the target object to the method as the first block parameter (i.e. the self), imp_implementationWithBlock() returns a trampoline function that when called (via objc_msgSend()):
- slides the self reference into the slot for the selector (i.e. arg 0 -> arg 1)
- finds the implementing block puts the pointer to that block into arg 0
- JMPs to the block's implementation pointer
The finds the implementing block bit is kinda interesting, but irrelevant to this question (hell, the imp_implementationWithBlock() is a bit irrelevant, too, but may be of interest).
Thanks for response. It's definitely an eye opener. The part about
blocks calling is not done thru objc_msgSend() tells me that it is
because blocks are not part of the normal object-hierachry (but coda's
mentioning of NSBlock seems to refute what I understand so far,
because NSBlock would make it part of the object-hierachy). Feel free
to take a stab at me, if my understanding is still off so far. I am
very interested in hearing more about the followings 1: the SPI and
the way (how) to call that method. 2: the underlying mechanisms of:
sliding the self reference into the slot. 3: finds the implementing
block and puts the pointer to that block into arg 0. If you have time
to share and write a bit more about those in detail, I am all ears; I
find this all very fascinating. Thanks very much in advance.
The blocks, themselves, are very much just a standard Objective-C object. A block instance contains a pointer to some executable code, any captured state, and some helpers used to copy said state from stack to heap (if requested) and cleanup the state on the block's destruction.
The block's executable code is not invoked like a method. A block has other methods -- retain, release, copy, etc... -- that can be invoked directly like any other method, but the executable code is not publicly one of those methods.
The SPI doesn't do anything special; it only works for blocks that take no arguments and it is nothing more than simply doing block().
If you want to know how the whole argument slide thing works (and how it enables tail calling through to the block), I'd suggest reading this or this. As well, the source for the blocks runtime, the objc runtime, and llvm are all available.
That includes the fun bit where the IMP grabs the block and shoves it into arg0.
Yes, blocks are objects. And yes, that means you can send messages to them.
But what message do you think a block responds to? We are not told of any messages that a block supports, other than the memory management messages retain, release, and copy. So if you send an arbitrary message to a block, chances are that it will throw a "does not recognize selector" exception (the same thing that would happen if you sent an arbitrary message to any object you don't know the interface of).
Invoking a block happens through a different mechanism than message sending, and is magic implemented by the compiler, and not exposed to the programmer otherwise.
Blocks and selectors are very different. (A selector is just an interned string, the string of the method name.) Blocks and IMPs (functions implementing methods) are somewhat similar. However, they are still different in that methods receive the receiver (self) and the selector called as special parameters, whereas blocks do not have them (the function implementing the block only receives the block itself as a hidden parameter, not accessible to the programmer).
I have a method that I want to debug:
-(void)doAThingWithObject:(BaseDataObject *)dataObject //called VERY often
And I have an Xcode breakpoint inside this method which I want to only break on a certain subclass of BaseDataObject, so I add a breakpoint w/conditional to check for that class:
[dataObject isKindOfClass:[SubClassOfBaseDataObject class]]
However, doing so results in a parse error!
Stopped due to an error evaluating condition of breakpoint 11.1: "[dataObject isKindOfClass:[SubClassOfBaseDataObject class]]"
Couldn't parse conditional expression:
error: no known method '+class'; cast the message send to the method's return type
error: 1 errors parsing expression
I have made sure to import all classes in the file, but the debugger does not know what class I'm referencing in the conditional.
However, creating a temp variable of said Class inside the method before the breakpoint:
Class subClassCheck = [SubClassOfBaseDataObject class];
And updating the breakpoint conditional to reference the temp variable:
[dataObject isKindOfClass:subClassCheck]
Throws no errors.
I'm a bit of a novice when it comes to breakpoint conditionals, can someone explain why my first approach doesn't work?
One complication with debugging code that is based on big frameworks like Cocoa is that it is not practical for the compiler to emit or the debugger to consume every type and function in the whole closure of frameworks you include. So the compiler uses some heuristics to reduce the amount of debug information generated. It will emit type information only for types that you actually use, and function/ObjC method information where the method is defined (as opposed to declared in a header file.) There's another little subtlety that lldb will read the type information for methods out of the ObjC runtime, though this information is not complete, since it is meant for the runtime not for debuggers... So we sometimes seem to know things about ObjC methods that violate the previous rule.
Another important thing to note is that the calling conventions for functions that return something larger than a pointer (like NSMakeRect, etc) are such that if the debugger calls a function thinking it returns a pointer and it actually returns a bigger structure, that act will cause stack corruption in your program. If you are lucky you will crash right away when you continue, but if you are unlucky it will just change some data value and cause you to spend hours trying to chase down some funny behavior that is actually caused by the debugger. So the debugger will refuse to call functions whose return type it can't determine.
Anyway the error you got is because the debugger couldn't find debug information for the "+class" method on your object. That is not altogether surprising, since "class" is a method on NSObject and not your class. I'm not sure why we couldn't find it in the runtime, maybe because it is a class method? That's worth a bug. We obviously did get the type of isKindOfClass: from the runtime, or your workaround would have also failed.
In this case, since you actually know the return type of the class method, you can work around the debugger's lack of knowledge by explicitly casting it in your breakpoint expression. Casting a function return in the debugger's expression parser serves two purposes, one is the regular C language function, and the other is telling the debugger the return type of a function it wouldn't otherwise be able to figure out. A sort of short-hand prototype only for the return type.
So something like:
[dataObject isKindOfClass: (Class) [SubClassOfBaseDataObject class]]
should work without having to alter your code.
Note also, the breakpoint conditions are run using the same mechanism as the "expr" or "print" command. So the easiest way to experiment with breakpoint commands is to set an unconditional breakpoint, hit it, then go to the lldb console and play around with "print" till you get something that works.
I am parsing a JSON file to build an object graph. While deserializing the JSON, I validate the objects using blocks. I need to break out of further block processing if any validation error happens on any of the objects. Is there a way to break out of nested blocks - like break in a switch statement?
I'm running iOS 5.0 and using ARC. Please let me know if you would like some code to help understand my problem.
The solution to this is to add a __block BOOL ivar to your class, lets call it 'cancel'. When you find an error, set 'cancel' to yes. In every block, in every loop, only do work if 'cancel' is false.
This way, when the error is hit and cancel is set, the blocks will essentially quit as soon as they detect 'cancel' is set.
EDIT: since I wrote this ivars do not need the __block qualifier (and may never have needed it, not sure). When blocks reference ivars, they do it through a "self" pointer, i.e. self->ivar.