2

I'm writing a clone of OpenStruct in Objective-C, using forwardInvocation:. However, the compiler isn't aware of the forwarding at compile time apparently. Compiling with ARC gives me a ton of warnings.

The code is open source and available on Github, but is currently compiled with -fno-objc-arc. If anyone could take a look at how I could make this ARC compatible, I'd greatly appreciate it.

wjl
  • 7,143
  • 1
  • 30
  • 49
  • It would help if you posted at least one of the errors and the corresponding code where the compiler has an issue with. – CodeSmile Nov 03 '11 at 21:51
  • 1
    It is not possible to do what you’re trying to do. The compiler needs to know the method signatures at compile time and will complain if it doesn’t know them. – Chris Suter Nov 08 '11 at 02:12

1 Answers1

3

I tried this code:

OpenStruct *myStruct = [[OpenStruct alloc] initWithDictionary:myDictionary];
NSLog(@"%@ says %@", @"Cow",  [myStruct cowSound]);

I get warnings or errors with and without ARC, with LLVM 3.0 or LLVM GCC 4.2. I think you've misunderstood that forwardInvocation: still requires the method to be declared at some level, if only in a category (@interface) of the class you're sending the message to.

For example, when you do:

[someObject doSomething];

Then this will always generate at least a warning ("someObject may not respond to doSomething") if doSomething is not declared anywhere, regardless of whether the someObject class implements forwardInvocation or not. Like you noticed, the compiler is indeed not aware of the forwarding, and it also can't rely that your implementation of forwardInvocation guarantees message delivery. With LLVM 3.0 with or without ARC this may have been changed to generate an error instead, because ARC's development goal was to err on the side of more compiler errors rather than runtime issues.

Now you can still send messages to an object that doesn't implement a method. For example by using Objective-C runtime method objc_msgSend or via NSInvocation. But that voids the simple to use interface that you were planning to create.

Btw, your usage example for OpenStruct does not really justify why it is any simpler to access the dynamic struct via messaging compared to simple accessors like [struct getValueForKey:@"moo"]; … if you think this over then [struct moo] gives users little or no benefit over the first approach. The "moo" method is dynamic either way (a string or forwarded message) and a typo won't be caught at runtime.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • The benefit would simply be smaller code. I think it's more to the point. [cow sound] immediately says that you're interested in the sound the cow makes. – wjl Nov 04 '11 at 08:00
  • Sure but then you could just write a Cow class with a sound method, right? Or, better yet, an Animal class that implements the sound method, and the sound filename can be set on init or via a property. All other classes like Cow, Frog, Pterodactyl inherit from Animal and can subsequently play sound. – CodeSmile Nov 04 '11 at 23:12
  • The use case I'm thinking of is for an interface to a CMS. The content will be managed by a web application. The iOS application will sync to it. It's not possible to predefine all potential fields in the iOS code because they can be added and removed at will by the user through the web interface. Using dictionaries or key-value coding is clumsy syntax and a lot of typing compared with using the property syntax. – Jim Nov 07 '11 at 14:59
  • You can use the property syntax (eg. `myLabel.stringValue`) only with properties that are defined using `@property` in the header file. So as stated above, `-(id)getObjectForKey:` is most likely the better choice. Objective C is extremely verbose. It is generally advisable to embrace the best practices of a language rather than trying to circumvent them with dangerous hacks. – Jakob Egger Nov 07 '11 at 21:11
  • Your first point isn't true. `foo.bar` is equivalent to `[foo bar]` and works without any property declaration. On what basis are you calling this a "dangerous hack"? – Jim Nov 08 '11 at 00:47
  • foo.bar = 10 works if the -(void) setBar:(int)value {} setter method is implemented, likewise for the getter. I agree with Jakob that it is a dangerous hack because it would bypass any checks that the compiler can do and turn them into runtime errors (crashes) if they do occur. – CodeSmile Nov 08 '11 at 14:42
  • 1
    The same problems apply to the alternatives though. Objects in dictionaries have no type checking. Key-value coding has no type checking. Are they both "dangerous hacks" as well? – Jim Nov 08 '11 at 15:11
  • I'm calling it a dangerous hack because it uses a very complicated feature of the Objective C language for something it is clearly not intended to do, while at the same time, there's a proven, simple, standard way of accomplishing the ultimate goal (key-value data storing). It seemed to me as if you are trying to implement a whole new framework just because `-objectForKey:` is too much to type. – Jakob Egger Nov 13 '11 at 23:05
  • @JakobEgger: That an object can have the ability to handle any message that comes to it is one of the unique features of Objective-C, and has many uses. I wouldn't call it a dangerous hack. – user102008 Nov 21 '12 at 02:45
  • @user102008 It has many uses, but I doubt that re-implementing a dictionary is a good example. I'm not calling it a dangerous hack in general, just in this specific situation. – Jakob Egger Nov 24 '12 at 07:37