5 ms·
I know the feeling, but I think I have a bug that beats even this one... Once, I was working on the "alias" keyword in MacRuby, and getting it to work with met
by jballanc 14y ago
I know the feeling, but I think I have a bug that beats even this one...
Once, I was working on the "alias" keyword in MacRuby, and getting it to work with methods defined in Obj-C. Since MacRuby objects are just extensions on Obj-C objects, some methods are defined in Obj-C and require special casing in order to alias them to a different identifier. For example, if you want to alias "Array#length" to a different name, you'd actually be aliasing "-[NSArray length]".
So I do everything I should need to do to make it work: get a handle to the object, the object's class, the method defined for the class, a selector for the new name...but no matter what I tried, every time I aliased the method, and attempted to call the method by the new name, I would always get the same error: "Method not defined".
Finally, I decided to dive deep with gdb and find out exactly what was going on. To my shock and amazement, I came to realize that I was properly aliasing the method but, due to the nature of NSArray's implementation as a class cluster (and the fact that Obj-C doesn't have proper virtual methods like C++), the implementation of "-[NSArray length]" was literally:
- (NSInteger)length
{
[NSException raise:@"Method not defined." format:@"%"];
}
- eridius 14y agoAre you saying that when a name is aliased in MacRuby, the alias becomes a copy of the IMP from that particular class, such that subclass overrides of that method are never invoked? Because that sounds like a bad idea.
- jballanc 14y agoAliasing in MacRuby (or Obj-C for that matter) involves adding a new entry to a class's method table with the new name but a pointer to the old implementation. Method lookup from subclasses to superclasses proceeds the same as it would for any method.
- eridius 14y agoYes, but if you message the subclass with the alias, only the superclass's method gets invoked, because the alias apparently contains the IMP of the superclass's method. Or it must, if what you said before is to be believed. This is pretty nasty, because it breaks the idea of message sends and subclass overrides of methods. There's two ways you could solve it: 1. Set the IMP to a static function that looks up the method it should forward to and re-send the message there. This is hard to do in a manner that supports all arguments, and you'd need at least 2, possibly 3 versions anyway (one for regular, one for strret, and on some architectures, one for fpret). It would probably need to be written in assembly in order to forward the arguments. 2. More recently (in iOS 4.3, and whatever the corresponding OS X release was), the runtime got a nice little function called imp_implementationWithBlock(). This takes a block and returns an IMP. Bill Bumgarner has a nice blog post about it (http://www.friday.com/bbum/2011/03/17/ios-4-3-imp_implementationwithblock/ http://www.friday.com/bbum/2011/03/17/ios-4-3-imp_implementa...). You could use this function on every alias in order to invoke the correct method, passing along the correct arguments.
- jballanc 14y agoThat may be nasty, but that's how the semantics of method aliasing in Ruby work. Try this Ruby snippet to see what I mean: class Foo def say; puts "hello"; end alias shout say end class Bar < Foo def say; puts "howdy"; end end Foo.new.say #=> "hello" Bar.new.say #=> "howdy" Foo.new.shout #=> "hello" Bar.new.shout #=> "hello"
- eridius 14y agoOh ugh. When is an alias not an alias? When it's Ruby, I guess.