Cocoa Scripting: Intercept any Object-first message calls - objective-c

I am writing my own non-ObjC framework around Cocoa Scripting (think of writing a scriptable Mac app in C, C++ or Java, or in my case, Xojo).
I like to be able to intercept any Object-first method invocation instead of having to add the actual method to the ObjC class (I can't because the framework won't know which message the app code can handle in advance - so it'll instead have to receive and pass on any command message once they come in from the scripting engine).
For instance, any property getters and setters can be intercepted via implementing
-valueForUndefinedKey:
-setValue:forUndefinedKey:
as well as all the methods of the NSScriptKeyValueCoding protocol.
I am looking for a similar way to intercept NSCommandScript messages sent to the method specified in these sdef elements:
<responds-to command="reload">
<cocoa method="reloadList:"/>
</responds-to>
So, instead of implementing reloadList: by adding it to the class methods, I wonder if there's a generic way to catch all such calls.
I found that the class method
+ (BOOL)resolveInstanceMethod:(SEL)sel
gets invoked asking for reloadList:. But the same method is invoked for many other purposes as well, and so I rather not blindly intercept every such call because it would cause a rather severe performance hit if I'd forward them all to a Java function that tells me whether it wants to handle it, for instance.
I hope there's something that lets me tell that this selector is related to a NSScriptCommand before forwarding it further.

After setting a breakpoint into the designated command handling method, I saw the following stack trace:
#0 0x00000001000197db in -[SKTRectangle rotate:]
#1 0x00007fff8ee0b7bc in __invoking___ ()
#2 0x00007fff8ee0b612 in -[NSInvocation invoke] ()
#3 0x00007fff8eeab5c6 in -[NSInvocation invokeWithTarget:] ()
#4 0x00007fff8b82cbde in -[NSScriptCommand _sendToRemainingReceivers] ()
#5 0x00007fff8b82cf39 in -[NSScriptCommand executeCommand] ()
This shows that NSScriptCommand does not appear to use any customizable special forwarding mechanism but uses NSInvocation to call the method.
Such invocations can be intercepted like this:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// Look for signatures with exactly one ":"
if ([[NSStringFromSelector(aSelector) componentsSeparatedByString:#":"] count] == 2) {
return [NSMethodSignature signatureWithObjCTypes:"#:##"];
} else {
return [super methodSignatureForSelector:aSelector];
}
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
id arg; // The first and only argument is expected to be of type NSScriptCommand*
[anInvocation getArgument:&arg atIndex:2];
if ([arg isKindOfClass:[NSScriptCommand class]]) {
NSLog(#"executing the command...");
// when finished, set the return value (which shall be an NSObject)
id result = nil;
[anInvocation setReturnValue:&result];
} else {
// oops - we cannot handle this
[super forwardInvocation:anInvocation];
}
}
This form of interception works better than using resolveInstanceMethod: because it doesn't get called so often but only for specific purposes such as an NSScriptCommand execution.
The problem with this, however, is that if other code also uses NSInvocation to make calls into the same class for other purposes, and if those calls use a matching selector signature, the above code would intercept those calls and then not handle them, possibly leading to unexpected behavior.
As long as the classes are known to be used only by the scripting engine and have no other behavior (i.e. they're immediate subclasses of NSObject), there is no reason for this to happen. So, in my special case where the classes act only as proxies into another environment, this may be a viable solution.

If it's not a Cocoa-based app then you're probably best to forget about using Cocoa Scripting as it's heavily coupled to the rest of the Cocoa architecture, install your own AE handlers directly using NSAppleEventManager and write your own View-Controller glue between those and whatever you eventually implement your Model in. See also: Scriptability (AppleScript) in a Mac Carbon application
ETA: Come to think of it, you might want to rummage around the web and see if you can dredge up any old C++ AEOM frameworks, as ISTR there were one or two around back in the pre-OS X days. May require some updating, and may or may not be any good (but then CS is rather crappy too), but it'd be far easier than starting completely from scratch as designing and implementing a good, robust, idiomatic (or even simplified) AEOM framework is a giant PITA, even when you do know what you're doing (and hardly anyone does).

Related

Objective-C: How to force a call to `+initialize` at startup rather than later when the class happens to used for the first time?

Problem
For certain classes, I would like to explicitly call the +initialize method when my program starts, rather than allowing the runtime system to call it implicitly at some nondeterministic point later when the class happens to first be used. Problem is, this isn't recommended.
Most of my classes have little to no work to do in initialization, so I can just let the runtime system do its thing for those, but at least one of my classes requires as much as 1 second to initialize on older devices, and I don't want things to stutter later when the program is up and running. (A good example of this would be sound effects — I don't want sudden delay the first time I try to play a sound.)
What are some ways to do this initialization at startup-time?
Attempted solutions
What I've done in the past is call the +initialize method manually from main.c, and made sure that every +initialize method has a bool initialized variable wrapped in a #synchronized block to prevent accidental double-initialization. But now Xcode is warning me that +initialize would be called twice. No surprise there, but I don't like ignoring warnings, so I'd rather fix the problem.
My next attempt (earlier today) was to define a +preinitialize function that I call directly instead +initialize, and to make sure I call +preinitialize implicitly inside of +initialize in case it is not called explicitly at startup. But the problem here is that something inside +preinitialize is causing +initialize to be called implicitly by the runtime system, which leads me to think that this is a very unwise approach.
So let's say I wanted to keep the actual initialization code inside +initialize (where it's really intended to be) and just write a tiny dummy method called +preinitialize that forces +initialize to be called implicitly by the runtime system somehow? Is there a standard approach to this? In a unit test, I wrote...
+ (void) preinitialize
{
id dummy = [self alloc];
NSLog(#"Preinitialized: %i", !!dummy);
}
...but in the debugger, I did not observe +initialize being called prior to +alloc, indicating that +initialize was not called implicitly by the runtime system inside of +preinitialize.
Edit
I found a really simple solution, and posted it as an answer.
The first possible place to run class-specific code is +load, which happens when the class is added to the ObjC runtime. It's still not completely deterministic which classes' +load implementations will be called in what order, but there are some rules. From the docs:
The order of initialization is as follows:
All initializers in any framework you link to.
All +load methods in your image.
All C++ static initializers and C/C++ __attribute__(constructor)
functions in your image.
All initializers in frameworks that link to you.
In addition:
A class’s +load method is called after all of its superclasses’ +load
methods.
A category +load method is called after the class’s own +load method.
So, two peer classes (say, both direct NSObject subclasses) will both +load in step 2 above, but there's no guarantee which order the two of them will be relative to each other.
Because of that, and because metaclass objects in ObjC are generally not great places to set and maintain state, you might want something else...
A better solution?
For example, your "global" state can be kept in the (single) instance of a singleton class. Clients can call [MySingletonClass sharedSingleton] to get that instance and not care about whether it's getting its initial setup done at that time or earlier. And if a client needs to make sure it happens earlier (and in a deterministic order relative to other things), they can call that method at a time of their choosing — such as in main before kicking off the NSApplication/UIApplication run loop.
Alternatives
If you don't want this costly initialization work to happen at app startup, and you don't want it to happen when the class is being put to use, you have a few other options, too.
Keep the code in +initialize, and contrive to make sure the class gets messaged before its first "real" use. Perhaps you can kick off a background thread to create and initialize a dummy instance of that class from application:didFinishLaunching:, for example.
Put that code someplace else — in the class object or in a singleton, but in a method of your own creation regardless — and call it directly at a time late enough for setup to avoid slowing down app launch but soon enough for it to be done before your class' "real" work is needed.
There are two problems here. First, you should never call +initialize directly. Second, if you have some piece of initialization that can take over a second, you generally shouldn't run it on the main queue because that would hang the whole program.
Put your initialization logic into a separate method so you can call it when you expect to. Optionally, put the logic into a dispatch_once block so that it's safe to call it multiple times. Consider the following example.
#interface Foo: NSObject
+ (void)setup;
#end
#implementation Foo
+ (void)setup {
NSLog(#"Setup start");
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(#"Setup running");
[NSThread sleepForTimeInterval:1]; // Expensive op
});
}
#end
Now in your application:didFinishLaunchingWithOptions: call it in the background.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(#"START");
// Here, you should setup your UI into an "inactive" state, since we can't do things until
// we're done initializing.
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[Foo setup];
// And any other things that need to intialize in order.
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"We're all ready to go now! Turn on the the UI. Set the variables. Do the thing.");
});
return YES;
}
This is how you want to approach things if order matters to you. All the runtime options (+initialize and +load) make no promises on order, so don't rely on them for work that needs that. You'll just make everything much more complicated than it needs to be.
You may want to be able to check for programming errors in which you accidentally call Foo methods before initialization is done. That's best done, IMO, with assertions. For example, create an +isInitialized method that checks whatever +setup does (or create a class variable to track it). Then you can do this:
#if !defined(NS_BLOCK_ASSERTIONS)
#define FooAssertInitialized(condition) NSAssert([Foo isInitialized], #"You must call +setup before using Foo.")
#else
#define FooAssertInitialized(condition)
#endif
- (void)someMethodThatRequiresInitialization {
FooAssertInitialized();
// Do stuff
}
This makes it easy to mark methods that really do require initialization before use vs ones that may not.
Cocoa provides a setup point earlier than +initialize in the form of +load, which is called very shortly after the program's start. This is a weird environment: other classes that rely on +load may not be completely initialized yet, and more importantly, your main() has not been called! That means there's no autorelease pool in place.
After load but before initialize, functions marked with __attribute__((constructor)) will be called. This doesn't allow you to do much that you can't do in main() so far as I know.
One option would be to create a dummy instance of your class in either main() or a constructor, guaranteeing that initialize will be called as early as possible.
Answering my own question here. It turns out that the solution is embarrassingly simple.
I had been operating under the mistaken belief that +initialize would not be called until the first instance method in a class is invoked. This is not so. It is called before the first instance method or class method is invoked (other than +load, of course).
So the solution is simply to cause +initialize to be invoked implicitly. There are multiple ways to do this. Two are discussed below.
Option 1 (simple and direct, but unclear)
In startup code, simply call some method (e.g., +class) of the class you want to initialize at startup, and discard the return value:
(void)[MyClass class];
This is guaranteed by the Objective-C runtime system to call [MyClass initialize] implicitly if it has not yet been called.
Option 2 (less direct, but clearer)
Create a +preinitialize method with an empty body:
+ (void) preinitialize
{
// Simply by calling this function at startup, an implicit call to
// +initialize is generated.
}
Calling this function at startup implicitly invokes +initialize:
[MyClass preinitialize]; // Implicitly invokes +initialize.
This +preinitialize method serves no purpose other than to document the intention. Thus, it plays well with +initialize and +deinitialize and is fairly self-evident in the calling code. I write a +deinitialize method for every class I write that has an +initialize method. +deinitialize is called from the shutdown code; +initialize is called implicitly via +preinitialize in the startup code. Super simple. Sometimes I also write a +reinitialize method, but the need for this is rare.
I am now using this approach for all my class initializers. Instead of calling [MyClass initialize] in the start up code, I am now calling [MyClass preinitialize]. It's working great, and the call stack shown in the debugger confirms that +initialize is being called exactly at the intended time and fully deterministically.

Forwarding call to foreign -init method

Situation
For a project of mine, I'm building some kind of extension. This extension must have a class that implements a method whose declaration is - (id)initWithBundle:(NSBundle *)bundle.
Issue
My extension has multiple classes, but the host app is so badly written that it calls - (id)initWithBundle:(NSBundle *)bundle on different classes, randomly.
I'm not willing to reduce the number of classes, so the only solution left would be to somehow forward to caller to the class that actually implement - (id)initWithBundle:(NSBundle *)bundle (A bit like a HTTP 302). I found many resources on forwarding calls, but not such thing as forwarding an -init method...
init is allowed to return an object other than itself. While I highly recommend fixing the calling code (I can't imagine a case where allowing for code that calls anything "randomly" is even a reasonable idea), if you want to return some other object from init, it works like this:
- (id)initWithBundle:(NSBundle *)bundle {
// I don't actually implement this, let's return the class that does
return [[OtherClass alloc] initWithBundle: bundle];
}
ARC will deal with throwing you away.
The caller now has the wrong type of course (they expect your type, and they have some other random object), which is very likely to lead to hijinks, very hard to track bugs, and general sorrow. But it's legal ObjC. And if the two classes have enough overlap of their methods, it might even work.
In normal cases, this pattern is called a Class Cluster. See What exactly is a so called "Class Cluster" in Objective-C? for some examples.

How does KVC deal with speed and errors?

I've been reading about KVC and Cocoa Scripting, and how properties can be used for this. I have a model class in mind, but the element/property data has to be obtained from the Internet. But the design of properties and KVC looks like it assumes fast & in-memory retrieval, while network calls can be slow and/or error-prone. How can these be reconciled?
For speed, do we just say "screw it" and post a waiting icon? (Of course, we should keep things multi-threaded so the UI doesn't stop while we wait.)
If your property is supposed to be always available, we could set it to nil if the resource call gets an error. But we would have no way to get the specifics. Worse would be a property that supports "missing values," then nil would represent that and we would have no spare state to use for errors.
Although Apple-events support error handling, I couldn't use it because between my potentially error-generating model calls and the Apple event, the KVC layer would drop the error to the floor (of oblivion). The Scripting Bridge API saw this problem, since its designers added a secret protocol to handle errors.
Am I wrong? Is there a way to handle errors with KVC-based designs?
Addendum
I forgot to mention exceptions. Objective-C now supports them, but the little I read about them implies that they're meant for catastrophic "instead of straight crashing" use, not for regular error handling like in C++. Except for that, they could've been useful here....
I think I understand what you're asking now. I would say using KVC (or property getters) is not a good way to accomplish what you're trying to do. If the code gets called on the main thread, you will then block that thread. which you don't want to do. As you have discovered you'll also have a hard time returning other state information such as errors.
Instead, you should use block syntax to create an asynchronous method that operates on a background queue. Here is a basic template for what this might look like:
// called from main thread
- (void) fetchDataInBackgroundWithCompletionHandler:(void (^)(id responseData, NSError *error))handler
{
// perform in background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^()
{
// perform your background operation here
// call completion block on main thread
dispatch_async(dispatch_get_main_queue(), ^{
if(// whatever your error case is)
{
handler(nil, error);
}
else // success
{
handler(responseData, nil);
}
});
});
}
This also gives you the benefit of being able to pass in as many other parameters are you want as well as return as many values as you want in the completion block.
Some very good examples of this pattern can be seen in AFNetworking, which is one of the more popular networking libraries written for iOS. All of the calls in the library can be made from the main queue and will return on the main queue asycnhronously while performing all networking in the background.

Is there a less repetitive way to forward action messages to another object?

I'm making a calculator app to learn Objective-C and maybe improve my OO design skills a bit. In an attempt to do things more MVClike, i have separated the actual do-the-calculator-stuff code from the view controller. For every action, pretty much all the view controller does is tell the "model" to do the operation meant for that action.
Thing is, that gives me a bunch of methods that do basically nothing but forward the action to the model, like this:
- (IBAction)clearAll:(id)sender {
[self.model clearAll];
}
- (IBAction)clearDisplay:(id)sender {
[self.model clearDisplay];
}
- (IBAction)clearMemory:(id)sender {
[self.model clearMemory];
}
- (IBAction)storeMemory:(id)sender {
[self.model storeMemory];
}
- (IBAction)addMemory:(id)sender {
[self.model addMemory];
}
- (IBAction) subtractMemory:(id)sender {
[self.model subtractFromMemory];
}
- (IBAction)recallMemory:(id)sender {
[self.model recallMemory];
}
Objective-C so far seems outrageously flexible with dynamically forwarding messages, and these methods are alike enough to look rather easily automated away. Do they really have to be there? Or is there a less repetitive way to tell the controller to just pass certain messages through to the model (ideally, while stripping off the sender arg)?
I've been looking a bit and trying some stuff with selectors and NSInvocation, but it seems like that'd mess with Interface Builder by taking away all the (IBAction) markers that let me hook up buttons to actions. (I'd prefer if the view didn't have to know or care that the controller's just forwarding to the model in these cases.)
So, is there a less repetitive and/or hacky way? Or is it not worth the trouble? (Or is it a bad idea in the first place? Or is this trying to make the model do too much? Or...)
You can do what Gabriele suggested and it is certainly an example of how dynamic ObjC can be, but you are likely better off avoiding it. As Gabriele said, you'd better know exactly what you are doing and definitely not to overuse such feature. And that often indicates that such feature is likely more trouble than it is worth.
The reality is that your calculator application is a quite contrived for the purposes of driving home the separation inherent to the Model-View-Controller pattern. It is a learning app, as you state.
In reality, no application is ever that simple. You will rarely, if ever, have a field of buttons where that the control layer blindly forwards said functionality on to the model.
Instead, there will be all manners of business logic in that control layer that will may do everything from automating various actions to validation (potentially by querying the model) to updating UI state in response to various actions.
Likely this code will be present from very early in the project, thus that generic forwarding mechanism will quickly become completely unused.
As well, such forwarding mechanisms become funnels full of pain when it comes to debugging. you no longer have a concrete spot to drop a breakpoint, but now have to add conditions. Nor do you have an easy means of finding all the places that might invoke or implement a particular method. As well, it makes following the control flow more difficult.
If you do find yourself with lots of repetitive boiler-plate code, it is more of a sign that your architecture is likely flawed than a sign that you need to inject a spiffy dynamic mechanism to reduce the repetitiveness.
As well, if you were to continue to flesh out your calculator app, how much of your coding time would have been spent doing those repetitive methods vs. all other features in your app? Likely, very very little and, because of their simplicity and convenience to debugging, it is unlikely that said repetitive methods are ever going to incur any significant maintenance cost whereas a spiffy-dynamic bit of trickery (which is very cool and I encourage you to explore that in other contexts) is pretty much guaranteed to require a "Huh. What was I thinking here?!" moment later on.
You can use the dynamic features of the language.
From the Objective-C Runtime Programming documentation
When an object can’t respond to a message because it doesn’t have a method matching the selector in the message, the runtime system informs the object by sending it a forwardInvocation.
So in your case you can implement the forward invocation method as follows
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([self.model respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:self.model];
else
[super forwardInvocation:anInvocation];
}
Note
You have also have to uniform your methods signatures. Either remove the sender parameter or add it to the model's method, otherwise respondsToSelector will return NO and the method won't be called.
In this case forwardInvocation will act as a dispatcher and it will try to send every message not implemented by your controller to the self.model object. If this is not responding to a selector it will call super, very likely resulting in an unrecognized selector exception.
I personally find it very elegant, even though you'd better know exactly what you are doing and definitely not to overuse such feature.

How can I prevent a method from being wrongly overridden

How can I prevent a method from getting overridden in a subclass, missing a call to its superclass' implementation within?.
I know calling [super methodName]; will solve my problem sometimes.
But if somebody else was to use my parent class and overrode my method, accidentally missing to call super, what can I do?
Some more explanations:
I create a viewcontroller VC1 which has a method -(void)indexDidChange:(int)index { }. I write some actions there which I need to perform every time. and I subclass this viewcontroller named as SVC1 in it I need -(void)indexDidChange:(int)index { } for doing some other actions but at the same time the VC1 -(void)indexDidChange:(int)index { } action also need to perform. So I need to call like,
-(void)indexDidChange:(int)index {
[super indexDidChange:index];
}
So I decide to change VC1 function like,
-(void)indexDidChange:(int)index {
[self currentIndexDidChange:(int)index];
}
-(void)currentIndexDidChange:(int)index { }
And I need -(void)currentIndexDidChange:(int)index { } to override and prevent -(void)indexDidChange:(int)index { } from overriding.
Is it possible?
Edit: After OP rephrased the question it is clear that OP is actually NOT looking for final methods, despite the questions initial phrasing, which implied just this.
New (updated) answer to OP's question on method overriding safety:
According to your rephrased question you are not looking for protecting a method from being overridden at all, but rather worried about one of your subclasses overriding a method and accidently missing to include a call to super in its new implementation.
This however is a fairly common and widespread issue and something you're dealing with on a daily basis, without paying much attention to it.
Every Objective-C programmer is familiar with the following method, right?
- (void)dealloc {
[iVar release], iVar = nil;
[super dealloc]; //skipping this call to super is fatal!
}
And we al know that skipping the [super dealloc]; makes things get uncomfortable. (afaik the clang compiler issues a warning if dealloc lacks the call to super, …pretty handy.)
Despite the fact that a bad overriding of this method can have fatal consequences Apple did not choose to put any kind of security system in place here.
Instead Apple did this (as done with any other method requiring calls to super):
Add a note to the method's documentation:
After performing the class-specific
deallocation, the subclass method
should incorporate superclass versions
of dealloc through a message to
super
Expect you, the programmer, to be a grown-up and responsible for what you do. And for playing by the rules (as defined by the documentation).
Keep in mind that - (void)dealloc is by no means an exception. There are dozens and dozens of methods of this type in Cocoa. (Take just about any derivative of - (id)init, most of the KVO observing methods, etc. just to name a few.)
So what you should do is:
Write a good documentation for your
method. (better for your entire project, actually)
Add a big loud note to your method's documentation, explaining its rules.
Add a note to each of your subclasses' overridden method implementations, right above the line that's calling super, telling the reader/dev to look up documentation, when in doubt of the rules. (optional)
Code responsibly. Otherwise, you shouldn't be coding in first place. It's your customers who will suffer from it, eventually.
Old (pre-rephrasing) answer on archieving pseudo-final methods:
What you are asking for is the equivalent of a final function, as known from Java or C++.
Unlike Java or C++, however there are no final methods in Objective-C.
Depending on your situation there are solutions that might bring your at least near to what you're aiming for. All you'll get though is slightly better separation. You won't get any significant security from them. In Objective-C you cannot even be sure about the origin of your methods. Method swizzling allows you to exchange methods at will. With code injection you an even inject code into processes at runtime. All this is by design of Objective-C. Objective-C allows you to saw off the branch you're sitting on. Thus it demands you to act like a grown-up. As such there are no private methods either. If a method is proclaim private you as a dev are expected to behave accordingly.
Now to possible "solutions":
If only your super class if supposed to call the given (final) method anyway:
Then Macmade's solution of making your method a pseudo-private method would work quite well. The downside of hiding method declarations though is, that calling your hidden method from subclasses will give you a compiler warning, basically preventing*(sic!)* you from calling it. (It will not prevent you from calling the method though. It will only avoid you from doing so, by throwing compiler warnings.)
If subclasses however are expected to call the given (final) method:
Use a delegation pattern and by this only make those methods public that are allowed to be overridden.
To prevent overriding at all you could use the class cluster & abstract factory patterns, which hides your implementation classes and thus preventing overriding entirely. (Apple's NSArray, NSDictionary, NSSet classes do this)
However you might notice that with Objective-C lack of protection one usually can only choose between the two: openness, protectedness, not intermix them.
You can use categories in the implementation, so your methods aren't exposed in your header file.
MyClass.m
#interface MyClass( Private )
- ( void )myMethod;
#end
#implementation MyClass( Private )
- ( void )myMethod
{}
#end
#implementation MyClass
/* ... */
#end
If you don't declare your function in the ".h file" then its not listed, I think.