Joy Online Manual
NAME |
@teach - Teach new methods to an existing Objective-C class or object |
SYNOPSIS |
@teach unary-expr [<protocol1, protocol2, ...>]
[-|+][(type)] method-selector [declaration-list] {
statements
}
[-|+][(type)] method-selector [declaration-list] {
statements
}
...
@end
object.selector = function
object["selector:selector:..."] = function
DESCRIPTION |
Joy allows you to add new methods or override existing methods of Objective-C classes and even single objects at run-time. The methods you implement are registered with the Objective-C run-time environment like any Objective-C method, and can be called transparently from Objective-C (or Joy's Tcl interpreter).
There are several different notations of teaching a method: @teach is most general, since it allows the specification of argument and return types, as well as protocols. The expression must evaluate to an Objective-C object or class (it is evaluated just once, even if you teach multiple methods). You specify the method prototypes exactly as in Objective-C (if you do not specify - or + it defaults to -). Methods defined with this syntax always have two hidden parameters self (which refers to the receiving object) and _cmd (which refers to the selector). You can implement methods taking a variable number of arguments by ending the argument list with an ellipsis (...), as in Objective-C. From the method you can access the additional arguments via the arguments array (don't forget that argument indices 0 and 1 are used by self and _cmd). If you want such a method to be callable from Objective-C, all variable arguments have to be of the same type as the last fixed argument, their promoted size has to equal that of a void *, and you have to follow the convention of always passing NULL (or nil) as the last argument. Otherwise, Joy will not know how many arguments to convert and pass to JavaScript. If you specify protocols, Joy will, after teaching the methods, check for each protocol if the class or object implements it correctly. If so, the protocol will be added to the class' or object's protocol list, otherwise an error will be reported. For compatibility with Objective-C, adding new methods using @implementation is also supported. You can also teach methods by assigning a JavaScript function to any property of an Objective-C object. The selector name will be the same as the property name (if it contains colons you have to quote it and use array notation). The return and argument types will be taken either from the existing implementation of the same selector, or from the function (if it was declared with a prototype or is itself an Objective-C method), or they default to all id's. If the function has self and/or _cmd as its first/second parameter, Joy will correctly pass the receiving object/selector on method execution. Joy will try to resolve unqualified variable names in Objective-C methods first as local variables, then as parameters (including self and _cmd), then as instance variables of the receiving object, and then as global variables of the interpreter that executed the @teach or method assignment. This differs from the dynamic scoping used in JavaScript functions. If you have more than one Joy interpreter in your application, the method will always execute in the context of the interpreter that defined it. When an interpreter is deleted, all methods defined by it are automatically deleted. You can remove any JavaScript method implementation at any time by using the ObjC.unteach function or JavaScript's delete operator in the interpreter that defined it. Any former implementation of a selector that was overridden will then reappear. |
EXAMPLES |
Suppose you have an NSButton object button and an NSTextField object text in your application. You want the text field to display the string "Joy" if the button is pressed. We could use the following code: |
@teach button
-(void) action:(id)sender {
[text setStringValue: "Joy"]
}
@end
Since the selector action: is already defined for all NSButtons, you could also use a JavaScript assignment: |
button["action:"] = function(sender) {
[text setStringValue: "Joy"]
}
Unneeded function arguments may be omitted: |
button["action:"] = function() {
[text setStringValue: "Joy"]
}
If you want another button with the same behaviour, you could simply assign the existing method: |
button2["action:"] = button["action:"]
Of course you could chain the assignments together, like: |
button2["action:"] = button["action:"] = function() {
[text setStringValue: "Joy"]
}
Suppose you have an NSTextField object counter to count the number of characters in the NSTextView text. We could use the following code to overwrite the text view's existing didChangeText method (using former to call the original version): |
@teach text
-(void) didChangeText {
[former didChangeText]
[counter setIntValue: [[self string] length]]
}
@end
This is how it looks as a JavaScript assignment: |
text.didChangeText = function(self) {
[former didChangeText]
[counter setIntValue: [[self string] length]]
}
TIP: You can look at the current implementation of a method at any time, just by typing its name into the command window as a JavaScript property. The Joy interpreter will decompile the method's byte code for you: |
js> text.didChangeText
- (void)didChangeText {
[former didChangeText];
[counter setIntValue:[[self string] length]];
}
If no JavaScript implementation exists, you can at least look at the prototype: |
js> text["insertText:"]
- (void)insertText:(id)arg0;
SEE ALSO |
Objective-C Message Expression @class @implementation ObjC.unteach |
Index |