Is it possible to dynamically define methods in Objective-C like we would in Ruby?
[:failure, :error, :success].each do |method|
define_method method do
self.state = method
end
end
Actually it is possible to do this, although it's not supported by the obj-c syntax, the obj-c runtime provides functions that can do it. The one you want is class_addMethod, but off the top of my head i cannot remember the exact specifics of how. All the runtime methods are documented at developer.apple.com
For the hell of it I banged up a very simple example
#import <objc/objc-class.h>
#interface MyClass : NSObject {
}
#end
#implementation MyClass
#end
id myMethod(id self, SEL cmd, NSString* message)
{
NSLog(message);
return nil;
}
int main(int argc, char *argv[])
{
class_addMethod([MyClass class], #selector(newMethod:), (IMP)myMethod, "v#:#");
[[[MyClass alloc] init] newMethod:#"Hello World"];
return 0;
}
Now strictly speaking i think myMethod should be varargs, and it just happens to be okay to do it the way i am on x86, and may fail on ppc -- but then i could be wrong.
Oh here's the awful type encoding docs
I don't beleive it's possible, because Objective C is, after all, a compiled language. Your "define method" which have to add methods to the table and have a way to compile the method at run time.
Related
Here is a code snippet from Learning objective-c 2.0
Full code:
ClassWithFloat.h
#import <Foundation/Foundation.h>
#interface ClassWithFloat : NSObject
{
float value;
}
-(void)setValue:(float)aValue;
#end
ClassWithFloat.m
#import "ClassWithFloat.h"
#implementation ClassWithFloat
-(void)setValue:(float)aValue
{
value = aValue;
}
#end
ClassWithInt.h
#import <Foundation/Foundation.h>
#interface ClassWithInt : NSObject
{
int value;
}
-(void)setValue:(int)aValue;
#end
ClassWithInt.m
#import "ClassWithInt.h"
#implementation ClassWithInt
-(void)setValue:(int)aValue
{
value = aValue;
}
#end
main.m:
#import <Foundation/Foundation.h>
#import "ClassWithFloat.h"
#import "ClassWithInt.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
id number = [[ClassWithInt alloc] init];
[number setValue:3];
}
return 0;
}
failed to compile, after changing to ClassWithInt* number it works.
Error message:
/Users/jcyangzh/src/oc/SameName/SameName/main.m:17:9: Multiple methods named 'setValue:' found with mismatched result, parameter type or attributes
But since objective-c is somehow a dynamic programming language, the message call will be translated to native C method call.
obj_msgSend(number, #selector(setValue:), 3)
the obj_msgSend method find the class structure for the number object by isa variable. Which should make no difference between id or ClassWithInt type.
Why objective-c compiler could not recognize the right method?
Note: I am asking this question, because having same method name, but different argument type for different class is reasonable to me. But it seems that it is not possible either because the compiler limitation or the language design (do not supporting method overloading etc).
The problem really is that your object is only typed as id within the lexical scope.
The compiler doesn't know which method of the same name/selector to use.
You have multiple classes that have that selector but with different signatures because their arguments are different types.
You should avoid id in this case
Or typecast your object in the message send brackets to tell the compiler what class's method to use
Or
Bracket the same message call repeatedly in a sequence of if ([obj isKindOf:
checks. (Crazy here)
Or
Best take a hint from NSNumber class on good method naming conventions and do something like setFloatValue: and setIntValue: which is more readable and clear and helps the compiler a bit.
But any time you have and id type only, you need to be checking if the object isKindOf: or you are asking for trouble.
It is very very bad to have methods with same name but different signatures. (It is documented somewhere but I can't find now)
The calling conversion between calling setValue:(float) is different to setValue:(int), compiler have to generate different binary code.
As you said, it end up with something like
obj_msgSend(number, #selector(setValue:), 3)
but they are different
obj_msgSend(number, #selector(setValue:), (int)3)
obj_msgSend(number, #selector(setValue:), (float)3.0f)
Compiler have to decide at compile-time to generate the which version. Because the calling conversion between pass parameter with int type and float type are different.
Given code
ClassWithInt *number = [[ClassWithInt alloc] init];
[number setValue:3];
Compile know it need to generate the version with int with the help of type information.
but without type information
id number = [[ClassWithInt alloc] init];
[number setValue:3]; // is this takes int or float? if it is float then 3 need to be convert to float value first
There are two possible way to call it. Compiler can't figure it out without help. Hence the error message.
I want to use Protocol Buffers in an iOS project. I'm trying to avoid making the whole project into an Objective-C++ fiasco, so I want to wrap the C++ protobuf classes into Objective-C ones. I have several dozen protobuf messages, and while I have done this successfully one class at a time, ideally I would like to use inheritance to minimize the repeated code. I'm new to Objective-C and I haven't used what little I knew of C++ in 10 years, so this has mostly been an exercise in frustration. Below is an example of how I have wrapped a single message.
Code
.proto:
message MessageA {
optional string value = 1;
}
MessageAWrapper.h:
#import <Foundation/Foundation.h>
#interface MessageAWrapper : NSObject
#property (nonatomic) NSString *value;
+ (id)fromString:(NSString *)string;
- (NSString *)serialize;
#end
MessageAWrapper.mm:
#import "MessageA.h"
#import "message.pb.h"
#interface MessageAWrapper ()
#property (nonatomic) MessageA *message;
#end
#implementation MessageAWrapper
- (id)init
{
self = [super init];
if (self) {
self.message = new MessageA();
}
return self;
}
- (void)dealloc {
delete self.message;
self.message = NULL;
}
- (NSString *)value {
return [NSString stringWithUTF8String:self.message->value().c_str()];
}
- (void)setValue:(NSString *)value {
self.message->set_value([value UTF8String]);
}
- (NSString *)serialize {
std::string output;
self.message->SerializeToString(&output);
return [NSString stringWithUTF8String:output.c_str()];
}
+ (id)fromString:(NSString *)string {
MessageA *message = new MessageA();
message->ParseFromString([string UTF8String]);
MessageAWrapper *wrapper = [[MessageAWrapper alloc] init];
wrapper.message = message;
return wrapper;
}
#end
Goal
There is a lot of code here that will be repeated dozens of times in which the only variation is the wrapped class type (init, dealloc, serialize, fromString), so ideally I would like to put it on a parent ProtobufMesssage class instead. Unfortunately I've had no success in making this work because I can't find a way for the parent class to know the class its children are using, which is required for example in init and fromString.
Things I've attempted
struct
template class
void*
Obstacles I've encountered
can't find a way to store a reference to a class/type
can't have any C++ headers or code in the .h file (as this requires the whole project to be Objective-C++)
difficulty keeping references to the protobuf message parents (Message or MessageLite) because they are abstract
As I said I have very little understanding of C++ or Objective-C; most of my experience is with much higher level languages like Python and Java (though I do mostly understand basic C things like pointers).
Is this perhaps not even possible? Am I approaching it wrong or missing something obvious? Any help would be much appreciated. Thanks.
I don't know much about C++ at all, but can't you declare the Objective-C property to be a Message *?
You've already separated the C++ code from the header by declaring the property in the .mm file, the problem you will have is with instance methods named by the compiler (value() and set_value()) and only being valid methods for the subclass. It might help to use the Reflection class to get and set fields by their name. Here is an excerpt from Google's message.h showing this:
Message* foo = new Foo;
const Descriptor* descriptor = foo->GetDescriptor();
const FieldDescriptor* text_field = descriptor->FindFieldByName("text");
assert(text_field != NULL);
assert(text_field->type() == FieldDescriptor::TYPE_STRING);
assert(text_field->label() == FieldDescriptor::LABEL_OPTIONAL);
const Reflection* reflection = foo->GetReflection();
assert(reflection->GetString(foo, text_field) == "Hello World!");
You could create Objective-C -objectForKey: and -setObject:forKey: instance methods that typecheck and get or set the value (confusingly, the key in the case of MessageAWrapper would be #"value"). Your subclasses would not even need to be aware of the C++ code.
You can also separate the creator function in -init and +fromString: method into something like, +_createNewInstance;
+(Message*)_createNewInstance{ return new MessageA(); }
allowing your subclasses of MessageWrapper to reuse all code except for creating the C++ object.
While Objective C has very powerful instrospection capabilities, C++ is more limited. You do have RTTI (Run time type information), but it's not even as powerful as the Objective C counterpart.
However, it might be enough for you. Within your Objective C++ class, you might find the type of you message object with the typeid operator:
if( (typeid(self.message) == typed(foo)){
//doSomething
else if( (typeid(self.message) == typed(bar)){
// doSomething else
}
Maybe the best option is to add another indirection level. Make an Objective C class hierarchy that wraps all your protocol buffer C++ classes and then create another Objective C that uses those classes (as delegates maybe). I believe this might be a better option. Use C++ only for those unavoidable cases.
Good luck!
Although the overloading of # begins to tread on dangerous territory, I love the addition of the new Objective-C literals in Clang 3.1. Unfortunately the new literals are of limited use to me. Except for instances where code needs to interface with AppKit, I've mostly dropped the use of Foundation classes in favor of my own custom framework (for a variety of reasons; most of which is that I need direct control over the memory allocation patterns used by objects).
I could always use some runtime trickery to pass off the newly created object as my custom class (and is what I already have to do with string object literals, since only the non-Apple GCC runtime supports the -fconstantstring=class flag), but this is a hack at best and throws out all the benefits I gained by replacing the equivalent Foundation class to begin with.
Unlike string object literals, the new literals Clang implements are not actually constant classes (where the memory layout is hardcoded); instead the appropriate messages are sent to their respective classes to create and initialize a new object at runtime. The effect is no different than if you had created the object yourself. In theory it means that the classes used and the methods called by the new literals are not hardcoded. In practice I can't find any way to change them to point to my own custom classes and methods (I would in fact be happy just to point to a custom class; pointing a dummy method to an actual method at runtime isn't difficult).
When I first looked into this, I was really hoping to find a set of flags that could be used to do what I'm asking, but as I haven't found any, I'm hoping someone has a solution.
You can substitute class for some Objective-C literals with #compatibility_alias keyword trick.
Here's an example.
#compatibility_alias NSNumber AAA;
Of course, you should provide proper implementation for new class.
#import <Foundation/NSObject.h>
#interface AAA : NSObject
+ (id)numberWithInt:(int)num;
#end
#implementation AAA
+ (id)numberWithInt:(int)num
{
return #"AAAAA!!!"; // Abused type system just to check result.
}
#end
#compatibility_alias NSNumber AAA;
Now Clang will do the job for you. I confirmed this is working for number, array, dictionary literals. Unfortunately string literals seem to be emitted statically, so it won't work.
For more information about #compatibility_alias keyword, see here.
Note
Because #compatibility_alias keyword is a compiler directive which applies to current compilation unit, you need to separate compilation unit to avoid symbol duplication with NSObject class in Apple's Foundation Kit. Here's how I did it.
main.m
#import "test.h" // Comes before Foundation Kit.
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool
{
NSLog(#"return of test = %#", test());
// insert code here...
NSLog(#"Hello, World!");
}
return 0;
}
test.h
id test();
test.m
#import "test.h"
#import <Foundation/NSObject.h>
#interface
AAA : NSObject
+ (id)numberWithInt:(int)v;
+ (id)arrayWithObjects:(id*)pobj count:(int)c;
+ (id)dictionaryWithObjects:(id*)pvals forKeys:(id*)pkeys count:(int)c;
#end
#implementation AAA
+ (id)numberWithInt:(int)v
{
return #"AAAAA as number!!!";
}
+ (id)arrayWithObjects:(id*)pobj count:(int)c
{
return #"AAAAA as array!!!";
}
+ (id)dictionaryWithObjects:(id*)pvals forKeys:(id*)pkeys count:(int)c
{
return #"AAAAA as dictionary!!!";
}
#end
#compatibility_alias NSDictionary AAA;
#compatibility_alias NSArray AAA;
#compatibility_alias NSNumber AAA;
id test()
{
// return #{};
// return #[];
return #55;
}
Result.
2013-03-23 08:54:42.793 return of test = AAAAA!!!
2013-03-23 08:54:42.796 Hello, World!
The comments have it all correct, but just to summarize:
No.
The meanings of Apple's #{}, #[], and #"" literals are hard-coded into Clang. You can see it here: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/NSAPI.cpp?view=markup It's all fairly modular, meaning that it wouldn't be hard for a Clang hacker to add her own literal syntax... but "modular" doesn't mean "accessible from the outside". Adding a new syntax or even redirecting the existing syntax to new classes would definitely require rebuilding Clang yourself.
Here's a blog post about adding NSURL literals to Clang by hacking on its internals: http://www.stuartcarnie.com/2012/06/llvm-clang-hacking-part-3.html (Thanks #Josh Caswell)
If you're willing to use Objective-C++ with C++11 extensions, you can has "user-defined literals", which allow you to write things like
NSURL *operator ""URL (const char *s) { return [NSURL URLWithString: #(s)]; }
int main() {
...
NSURL *myurl = "ftp://foo"URL;
...
}
This was mentioned in the comments on Mike Ash's blog. http://www.mikeash.com/pyblog/friday-qa-2012-06-22-objective-c-literals.html But this doesn't look very Objective-C-ish (or very C++ish!), and it works only with an Objective-C++11 compiler, and in general please don't do this. :)
Is there an Objective-C equivalent of C++'s dynamic_cast?
It can be faked using this:
MyClass *safeObject = [object isKindOfClass: [MyClass class]]
? (MyClass *)originalObject : nil;
But this is a lot of code to type, even if I don't need to type it often.
I am a bit rusty so this might not be quite right, but I believe the equivalent in C++ would be:
MyClass safeObject = dynamic_cast<MyClass>(orginalObject);
The context here is a block where the parameter is defined as a type of a more generic class, but in this block I "know" it's a specific subclass. Nevertheless, I don't want to just cast it blindly with (MyClass *)originalObject and ignore a theoretically possible error.
To be clear, while I'd love a dynamic_cast, I'd be happy with an alternate approach to safely handle this case as well.
If you're willing to use Objective-C++, you can write this pretty easily:
template<typename T> inline T* objc_cast(id from) {
if ([from isKindOfClass:[T class]]) {
return static_cast<T*>(from);
}
return nil;
}
This should behave exactly as dynamic_cast<> except for obj-c objects.
If you want to stick with vanilla Obj-C you can get similar behavior with a class method on NSObject:
#interface NSObject (Cast)
+ (instancetype)cast:(id)from;
#end
#implementation NSObject (Cast)
+ (instancetype)cast:(id)from {
if ([from isKindOfClass:self]) {
return from;
}
return nil;
}
#end
This version just isn't as nice to use since you have to say something like
UIButton *button = [UIButton cast:someView];
In both versions the resulting value is nil if the cast fails.
Try this macro:
#define objc_dynamic_cast(obj, cls) \
([obj isKindOfClass:(Class)objc_getClass(#cls)] ? (cls *)obj : NULL)
And also don't forget to
#include <objc/runtime.h>
Use it like:
MyClass *safeObject = objc_dynamic_cast(originalObject, MyClass);
I don't think there is.
I think the space for a bug is quite small here.
But if you insist, a macro will do fine?
I'd like to write an Objective-C class without Cocoa or GNU's Object.h (for educational purposes). I dug around the net and it seems to me that quite a lot of stuff that one would expect to "come with the language", such as classes and message sending are actually defined in files written by third parties, such as objc-runtime.h.
Is there any documentation about what is really pure Objective-C and what is part of the runtime / frameworks? And what functionality do I have to implement to get a working environment without using any third-party code such as Object.h or objc-runtime.h (note again that this is for educational purposes, not for production code)?
Thanks for any insight!
Really, the only thing you must take care of yourself if you don't inherit from NSObject is object creation and destruction; methods otherwise behave the same way regardless of their parent class. Features like KVC and memory management are features of OpenStep/Cocoa, but not required as part of the language.
Here's a class from scratch:
#interface MyClass { // note the lack of a superclass here
#private Class isa;
}
+ (MyClass *)create;
- (void)destroy;
- (int)randomNumber;
#end
#implementation MyClass
+ (MyClass *)create {
return class_createInstance(self, 0);
}
- (void)destroy {
object_dispose(self);
}
- (int)randomNumber {
return rand();
}
#end
And here's how it could be used:
int main(int argc, char **argv) {
MyClass *foo = [MyClass create];
if (foo) {
printf("random! %i\n", [foo randomNumber]);
[foo destroy];
}
}
Edit: If you don't even want to use class_createInstance() and object_dispose(), you'll have to implement equivalents manually, as well as an equivalent of class_getInstanceSize() so you know how much memory an object occupies. But even if you manage that, don't think you've escaped the Objective-C runtime! Message dispatch is still entirely built on the C functions in the runtime, and Objective-C syntax is transformed into calls to those functions during compilation.
Matt Gallagher wrote a really cool post on writing a bare-bones Cocoa program. Since Objective-C is a superset of C, you can just do:
echo "int main(){return 0;}" | gcc -x objective-c -; ./a.out ; echo $?
Anyways, you probably would get a lot out of reading his post.
As far as avoiding the framework and creating your own base object goes, all you need to do is make sure that the first iVar is declared Class is_a and you could probably have a reasonable stab at replicating NSObject is by passing through to the runtime functions.
As far as avoiding the runtime library AND the framework goes, that's not really possible. Objective C (or at least, the bits that aren't just C) is a dynamic language. So pretty much everything it does that C doesn't do is handled by the runtime library.
It might be possible to build your own classes and objects using the 32bit runtime and the deprecated API, which doesn't abstract away the layout of classes, protocols, etc. to the extent that the modern runtime does (I've only really poked around with the modern runtime)
Perhaps you could create classes, add methods and allocate instances and by setting values in class_t structs and then using malloc() to allocate, although even then, you'd still be implicitly using the runtime function objc_msgSend every time you used the [obj selector] syntax -- unless you want to implement that as well, in which case you've just reimplemented the language yourself. The 'pure core' of the language you're looking for just is the runtime.
Here's an example of class, without using class_createInstance or object_dispose, or any other Objective-C Runtime (at least we don't call them directly).
#import <objc/objc.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
static Class __scratchClass = NULL;
#interface Scratch {
Class isa;
char *name;
}
+ (id) initialize;
+ (Scratch*) new:(const char*)strName;
- (void) sayHello;
- (void) destroy;
#end
#implementation Scratch
+ (id) initialize {
__scratchClass = self;
return self;
}
+ (Scratch*) new:(const char*) strName {
Scratch* pObj = (Scratch*)malloc(sizeof(Scratch));
if (!pObj) return NULL;
memset(pObj, 0, sizeof(Scratch));
pObj->isa = __scratchClass;
pObj->name = (char*)malloc(strlen(strName)+1);
strcpy(pObj->name, strName);
return pObj;
}
- (void) sayHello {
printf("Hello, World!\nThis is Scratch (%s)...\n", name);
}
- (void) destroy {
if (name) {
free(name);
name = NULL;
}
free(self);
}
#end
int main(int argc, char** argv) {
Scratch* ps = [Scratch new:argv[0]];
[ps sayHello];
[ps destroy];
return 0;
}
Compile the code with (assuming you save it as 'test1.m'):
gcc -o test1 test1.m -lobjc