Is it like if I "pass a message" between two objects and if they happened to be have been instantiated in different threads then the invoked method would still run in its object's thread?
Calling a method is resolved at compile time and the method can be expected to be present at run time. Message passing is resolved at run time and the receiver object does not need to understand how to handle the message.
The term "message passing" in Objective-C does not have anything to do with threading.
I found a good article about this topic here: http://iskren.info/reading/info/ObjC/reading/dynamite.html
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.
I have recently started getting acquainted with explicit Multithreading in swift. I am trying to understand the below method to dispatch a new thread for executing a selector. while I am able to use it successfully, what I don't understand is what is the significance of target in the signature of the method below? is that argument used to hold a monitor lock for thread safety like in java ? I tried referring to the documentation with not much help. I would really appreciate if someone can help me understand what's happening under the hood here.
(void)detachNewThreadSelector:(SEL)selector
toTarget:(id)target
withObject:(id)argument;
The documentation for aTarget says:
The object that will receive the message aSelector on the new thread.
This means that the selector will be called on the object that you pass as the target. It's no different than making any other method call. You call a method on a specific instance of a class. The target is the specific instance. The selector is the method that is called on that instance.
Think of detachNewThreadSelector:toTarget:withObject: as calling the given method of the given object, with the given argument (or ignore the argument if the method has zero parameters), but call the method on a newly created thread.
For example:
[NSThread detachNewThreadSelector:#selector(expensiveComputationWithObjects:)
target:someCalculatorObject
withObject:someVeryLargeArray]
The method thus provides a very convenient way of dispatching method calls on background threads (though it doesn't allow reusing an existing thread).
Another minor disadvantage is that the methods in discussions need to have at most one parameter, though this limitation can be circumvented by having the target method receive a structure (a dictionary or another class) that holds the actual arguments.
I used NSURLConnectionobject and called its method cancel sometimes.
Now I should replace NSURLConnection -> NSURLSession. NSURLSession operates with tasks which have cancel method too.
The problem is -[NSURLConnection cancel] just stop the handling of requests but if I use -[NSURLSessionTask cancel] it produces "cancelling error". So how to properly distinguish if cancel is called manually or if a real error is occurred?
I've found 3 solutions:
subclass task/session class
create a custom property via method swizzling in task/session class
the simplest but not very beautiful way - task has string property taskDescription and documentation says that developers are free to use it as they want.
Compare the error code to NSURLErrorCancelled. This error code is generated only when your code cancels the request.
I am creating a class named WeatherService that based on a location will provide the weather forecast (surprise!). There are multiple things that could go wrong here; the location is not found, weather service is not reachable, ...
The service has a method named weatherFor taking a location object as argument. It is asynchronous.
I am using Swift 2. So I have been reading about error handling in Swift 2.0 and there seems to be one way that multiple sources describe and that is a closure with ResultType with a success handler and an error handler. Should I use that? Or should I create a delegate and pass the error like many of the framework objects does such? I already have a delegate for the weather service since I need to know when it starts, when it is done etc.
The delegate protocol has methods like:
Started weather forecast search
Ended weather forecast search
But I don't know if I also should add a method in the delegate that is invoked when something bad happens inside the weather service communicating that an error occured and the weather forecast couldn't be found, or if that is bad? I guess delegates with "error methods" that are invoked isn't bad in Swift?
If you have only one other object interested in receiving notifications about progress, errors etc you can either go with a closure or delegate. It sort of depends on when and where the errors can occur. If you only need to pass on errors or state when making the weatherFor call you can use the closure solution. If errors can happen "anytime", even after - or before - getting the weather, you should use the delegate solution. Or a combination of both.
One other way of doing it is to post notifications when errors occur. This means that more than one object can listen in on updates at the same time.
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.