Is it possible to generate an Obj-C method? - For example:
- (void)doSomething:(BOOL)isLater
{
if (isLater == FALSE)
{
NSLog(#"Not Later");
} else {
NSLog(#"Is Later");
}
}
I'm specifically talking about generating the logic, I have some code that can create a class and add an existing method, but I'd like to know If I can generate the logic for the method itself and add it to a method?
static NSString *Description(id self, SEL _cmd)
{
return #"My Description Method";
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Class mySubclass = objc_allocateClassPair([NSObject class], "MySubclass", 0);
// grab NSObject's description signature so we can borrow it
Method description = class_getInstanceMethod([NSObject class],
#selector(description));
const char *types = method_getTypeEncoding(description);
// now add
class_addMethod(mySubclass, #selector(description), (IMP)Description, types);
objc_registerClassPair(mySubclass);
id myInstance = [[mySubclass alloc] init];
NSLog(#"%#", myInstance);
}
If we are splitting hair:
Yes, technically, you can.
But it won't be easy at all.
Objective-C can be parsed by libclang (the C++ library which the clang compiler is based on) and then JIT-compiled and run using LLVM.
This way, you can embed clang and LLVM into your program, generate some Objective-C source text dynamically, parse it into an abstract syntax tree (or generate that AST directly, without writing and parsing any Objective-C), and compiling it. If you write a method that does this, then you can essentially run arbitrary Objective-C code dynamically from within your application.
Related
In javascript, there is this concept of promises where you can pass a function as a parameter to another function. For example:
doSomething(doSomethingElse());
I have written a block based code in Objective-C. Example usage of the code is as follows:
[MyObject doSomethingWithSuccessHandler:^()
{
[My object doSomethingMoreWithSuccessHandler:^()
{
[myObject saveTheResults];
}
failureHandler:^(MYError *myError)
{
[MyObject logError:myError];
}];
}
failureHandler:^(MYError *myError)
{
[MyObject logError:myError];
}];
Instead of the way that my block based code is used as shown in the above snippet, I would like it to be used in the following way (like javascript promises):
[MyObject doSomethingAndOnSuccess:[myObject doSomethingMoreAndOnSuccess:[myObject saveTheResults] onFailure:[myObject logError] ] onFailure:[myObject logError] ];
Is this possible? It's just for the sake of readability of the code as you can see that in the first case there is a lot of pyramid code that I would like to avoid.
Take a look at Bolts by Facebook and Parse.
These will give you what you are looking for.
https://developers.facebook.com/blog/post/2014/01/30/let-s-bolt/
You can get it as a Cocoapod too to install it easily.
What you're passing still needs to be blocks rather than method calls but the typedef suggestion from the link in comments can make things clearer.
E.g.:
typedef void (^FailureBlock)(NSError *);
typedef void (^SuccessBlock)();
- (void)startsHere
{
FailureBlock f = ^(NSError *myError){
[self logError:myError];
};
SuccessBlock s2 = ^(){
[self saveTheResults];
};
SuccessBlock s1 = ^(){
[self doSomethingMoreWithSuccessHandler:s2
failureHandler:f];
};
[self doSomethingWithSuccessHandler:s1
failureHandler:f];
}
Let's say this is my init method
- (id)initWithClient:(id <Client>)client
andDataStorage:(DataStorage *)storage
{
if (self = [super init])
{
self.client = client;
self.storage = storage;
}
return self;
}
Then I want to write a macro that somehow logs the parameters passed to a method, by wrapping the parameter with a defined macro. Is this possible in any way?
The problem is at runtime it's not possible to find out the type of a parameter passed to a method. So I'm trying to find a hack around it, and do it at compile time.
// somehow achieve this, and log the value inside the marco
#define INJECT(x) NSLog(#"%#", x)
- (id)initWithClient:(INJECT(id <Client>))client
andDataStorage:(INJECT(DataStorage *))storage
{
}
expected log in console:
id <Client>
DataStorage *
At the risk of running into what appear to be crossed wires in the comments: you can get the parameter types passed to a method at runtime.
E.g.
NSMethodSignature *signature =
[class methodSignatureForSelector:#selector(someSelector:)];
for(int argument = 2; argument < signature.numberOfArguments; argument++)
{
const char *argumentType = [signature getArgumentTypeAtIndex:argument];
// this is where it gets a bit messy...
if(!strcmp(argumentType, #encode(int))) NSLog(#"an integer");
if(!strcmp(argumentType, #encode(float))) NSLog(#"a float");
// ... etc, etc, etc ...
}
For any passed objects, use [object class] since all objects look the same at the runtime level — think of e.g. NSArray -addObject:; the runtime knows an object type will be passed in but it could be any object type.
See Apple's documentation on Type Encodings for information on what's going on there with those #encodes.
Whilst not an answer to the question as such. I would not recommend doing what you are asking about. I've seen far to much code where people have logged every single method call and argument (horribly over-complicated Java Enterprise stuff). The result has always been obscenely large logs that tell you next to nothing because of the amount of work it takes to find what you are after.
My recommendation would be that logging is important, but you should do targeted logging that clearing shows the state of relevant data at specific points which are important to understanding the flow.
Like others, I'm not sure what you are really after, or whether it is a good idea/design etc. But I wonder whether you are approaching the problem the wrong way. So let's take a look and maybe it will help you. From what I see you:
Want to find some way of obtaining the declared types of method parameters, in the form of strings, at runtime.
You are trying to tackle this by adding macros to the source. This tells me that you are not trying to do this for methods in a binary library that you are dynamically loading, but to methods in source you are compiling and are prepared to modify to achieve your goal.
Looked at that way, what is the problem? If you are prepared to add macros to your source why not simply add data declarations that contain the information you want - a mapping from a selector to an order list of parameter types as strings.
Is the issue that you want to extract the information in some automated way and were intending adding your macros by some automated process?
You can arrange for an Xcode project to run a source file through some other program by changing the file extension. Apple provide examples of using this to pre-process strings files - the files are fed through a Ruby script which produces a strings file which Xcode then handles as usual. Will that address your needs? Could you write a script/application (doesn't need to be in Ruby) which could add the information you need "on the fly" - take source in, produce modified source out which Xcode then compiles as usual? Note that the Clang compiler itself is designed to be called as a library so you can even use it to help you parse your source to extract the information you are after.
If none of those approaches suit consider that the debugger knows the correct types at runtime, and it gets those from the symbol information generated for it. There are library functions provided to help reader debugger information, so you should be able to write code which uses the same information the debugger does.
Hope those ideas help you, though I'm still not clear what you are trying or whether it makes sense!
due to objC being dynamically typed, all classes have the type id. The information about the declared types is erased. They are merely hints for the developer and to enable the compiler to do some type checking (again purely for the dev's benefit)
So while #encode works for 'primates' and structs and stuff, for classes all is equal... as there are not really object types for runtime
'Solution': Store the class names of method argumentsin a map manually and then COMBINE that info with #encode;s info to log the stuff.
working sample:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
NSDictionary *DDParamsMap(void);
NSDictionary *DDParamsMap() {
static NSDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//TODO
//add all methods that are have objc classes passed
//add the classes or NSNull
dict = #{#"Test_initWithArray:data:number:": #[NSArray.class, NSData.class, NSNull.null]};
});
return dict;
}
void DDLogParamsOf(Class class, SEL sel);
void DDLogParamsOf(Class class, SEL sel) {
//
//try internal lookup first (so we get class names
//
NSString *className = #(class_getName(class));
NSString *methodName = NSStringFromSelector(sel);
NSString *key = [NSString stringWithFormat:#"%#_%#", className, methodName];
NSArray *types = DDParamsMap()[key];
//
// loop
//
NSMethodSignature *signature = [class instanceMethodSignatureForSelector:sel];
if(!signature) {
signature = [class methodSignatureForSelector:sel];
}
//if the array doesnt have the right number of values, screw it!
if(types.count != signature.numberOfArguments - 2) {
types = nil;
}
for(int argument = 2; argument < signature.numberOfArguments; argument++) {
id type = types[argument - 2];
if(type && ![type isKindOfClass:[NSNull class]]) {
NSLog(#"class is %#", type);
}
else {
const char *argumentType = [signature getArgumentTypeAtIndex:argument];
// this is where it gets a bit messy...
if(!strcmp(argumentType, #encode(int))) NSLog(#"an integer");
if(!strcmp(argumentType, #encode(float))) NSLog(#"a float");
if(!strcmp(argumentType, #encode(id))) NSLog(#"it is a class");
// ... etc, etc, etc ...
}
}
}
#define LogParams() DDLogParamsOf(self.class, _cmd);
#interface Test : NSObject
+ (void)testMethofWithFloat:(float)f;
- (id)initWithArray:(NSArray*)a
data:(NSData*)d
number:(int)i;
#end
#implementation Test
+ (void)testMethofWithFloat:(float)f {
LogParams();
}
- (id)initWithArray:(NSArray*)a
data:(NSData*)d
number:(int)i
{
LogParams();
return nil;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[Test testMethofWithFloat:3.0f];
Test *t = [[Test alloc] initWithArray:#[] data:[NSMutableData data] number:1];
t = nil;
}
}
Is it possible to create an Objective-C class that can have an arbitrary number of dynamic properties at runtime?
I want to be able to call mySpecialClass.anyProperty and intercept this inside my class to be able to provide my own custom implementation that can then return an NSString (for instance) at runtime with raising an exception. Obviously this all has to compile.
Ideal would be if I could refer to my properties using something similar to the new literal syntax, e.g. mySpecialClass["anyProperty"].
I guess in a way I want to create something like a dynamic NSDictionary with no CFDictionary backing store, that executes 2 custom methods on property getting and setting respectively, with the property name passed in to these accessor methods so they can decide what to do.
There are at least two ways to do this.
Subscripting
Use objectForKeyedSubscript: and setObject:forKeyedSubscript:
#property (nonatomic,strong) NSMutableDictionary *properties;
- (id)objectForKeyedSubscript:(id)key {
return [[self properties] valueForKey:[NSString stringWithFormat:#"%#",key]];
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key {
[[self properties] setValue:object forKey:[NSString stringWithFormat:#"%#",key]];
}
Person *p = [Person new];
p[#"name"] = #"Jon";
NSLog(#"%#",p[#"name"]);
resolveInstanceMethod:
This is the objc_sendMsg executed by the runtime for all methods:
If you look at the bottom, you have the opportunity to resolveInstanceMethod:, which lets you redirect the method call to one of your choosing. To answer your question, you need to write a generic getter and setter that looks-up a value on a dictionary ivar:
// generic getter
static id propertyIMP(id self, SEL _cmd) {
return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}
// generic setter
static void setPropertyIMP(id self, SEL _cmd, id aValue) {
id value = [aValue copy];
NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
// delete "set" and ":" and lowercase first letter
[key deleteCharactersInRange:NSMakeRange(0, 3)];
[key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
NSString *firstChar = [key substringToIndex:1];
[key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];
[[self properties] setValue:value forKey:key];
}
And then implement resolveInstanceMethod: to add the requested method to the class.
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) hasPrefix:#"set"]) {
class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v#:#");
} else {
class_addMethod([self class], aSEL,(IMP)propertyIMP, "##:");
}
return YES;
}
You could also do it returning a NSMethodSignature for the method, which is then wrapped in a NSInvocation and passed to forwardInvocation:, but adding the method is faster.
Here is a gist that runs in CodeRunner. It doesn't handle myClass["anyProperty"] calls.
You're asking different things. If you want to be able to use the bracket syntax mySpecialClass[#"anyProperty"] on instances of your class, it is very easy. Just implement the methods:
- (id)objectForKeyedSubscript:(id)key
{
return ###something based on the key argument###
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
{
###set something with object based on key####
}
It will be called everytime you use the bracket syntax in your source code.
Otherwise if you want to create properties at runtime, there are different ways to proceed, take a look at NSObject's forwardInvocation: method, or look at the Objective-C Runtime Reference for functions to dynamically alter a class...
Guillaume is right. forwardInvocation: is the way to go. This answer gives some more details: method_missing-like functionality in objective-c (i.e. dynamic delegation at run time)
This has even more details: Equivalent of Ruby method_missing in Objective C / iOS
And these are some other lesser known Obj-C features that might help you: Hidden features of Objective-C
Enjoy!
In Cocoa and Objective C the favorite method for managing error seems to be using an NSError * object, to construct an error object however, we need to call the following method
+ (id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dict
My question is, what are some of the best practices for managing error domain, error code definitions and user info dictionary across the entire application so that error code, domain and user info dict always stays consistent?
If you have a hefty amount of error construction, your life could be much simpler by using a class. I actually use C++ for this so the calls a program does not need may be removed (unlike objc), but you can use C, ObjC, or C++ for this:
MONErrorDomain.h
// you won't normally need an instance here
#interface MONErrorDomain : NSObject
+ (NSString *)domain; // << required override
- (NSString *)domain; // << returns [[self class] domain]
// example convenience methods:
// uses [self domain]
+ (NSError *)errorWithErrorCode:(NSInteger)errorCode; // << user info would be nil
+ (NSError *)errorWithErrorCode:(NSInteger)errorCode userInfo:(NSDictionary *)userInfo;
#end
MONKoalaError.h
#interface MONKoalaError : MONErrorDomain
+ (NSError *)outOfEucalyptus;
#end
extern NSString * const MONKoalaErrorDomain;
typedef enum MONKoalaErrorCode {
MONKoalaErrorCode_Undefined = 0,
MONKoalaErrorCode_OutOfEucalyptus
} MONKoalaErrorCode;
MONKoalaError.m
// apple recommends we use reverse domains
NSString * const MONKoalaErrorDomain = #"com.mon.koala-library.MONKoalaErrorDomain";
#implementation MONKoalaError
+ (NSString *)domain
{
return MONKoalaErrorDomain;
}
+ (NSError *)outOfEucalyptus
{
NSDictionary * info = …;
return [self errorWithErrorCode:MONKoalaErrorCode_OutOfEucalyptus userInfo:info];
}
#end
Then the error creation is all in one place for each domain, and the clients can easily pick their errors without actually building them manually:
if (outError) {
*outError = [MONKoalaError outOfEucalyptus];
}
and error handling takes the form:
if ([e.domain isEqualToString:MONKoalaErrorDomain]) {
switch (e.code) {
case MONKoalaErrorCode_OutOfEucalyptus : {
self.needsEucalyptus = true;
…
One common way is to define some appropriate constants in a header file, and then include that header file wherever needed. It's a pretty simple approach, and looks like:
const NSString * kMyAppErrorDomain = #"com.example.myapp";
const NSInteger kMyAppSomeError = 2;
// key into user info dictionary
const NSString * kMyAppProblemKey = #"MyAppProblemKey";
I've also seen some applications which create convenience methods for creating these, either as a category on NSError or as a separate utility class or set of functions. It's also entirely reasonable to subclass NSError, for example to customize the localized description.
If you have not already seen it, Apple has released the Error Handling Programming Guide which discusses how these should be used in Cocoa.
I am a Java programmer and learning objective C at the moment. For the purpose of creating JSON objects I want to translate the following Java-Pseudocode to objective C. However I got a huge amount of problems with that as it seems that everything I want is not available in objective C. My own variant takes up way more space and is way more complicated but as I am just a beginner I would like to know if there is a more simple and short way to achieve that.
Java Code
import java.util.HashMap;
import java.util.Map;
public class Parameters
{
public enum Language {deDE};
protected Language language = null;
// ... other parameters ...
protected static final Map<Language,String> languageToString = new HashMap<>();
static {languageToString.put(Language.deDE,"de-DE");}
Map<String,String> jsonProxy()
{
HashMap<String, String> map = new HashMap<>();
if(language!=null) {map.put("lang", languageToString.get(language));}
return map;
}
}
My Objective-C try
#interface Parameters : NSObject
enum Lang {deDE};
extern NSMutableDictionary *langToString;
- (id) proxyForJson;
extern int test;
#end
#implementation Parameters
enum Lang* lang = nil;
NSMutableDictionary *langToString;
-(CommunicationParameters*) init
{
self = [super init];
return self;
}
+(void) initialize
{
langToString = [[NSMutableDictionary alloc] init];
[langToString setObject:#"de-DE" forKey:[NSNumber numberWithInt:deDE]];
}
-(id) proxyForJson
{
NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init];
if(lang!=nil)
{
[dictionary setObject:[langToString objectForKey:[NSNumber numberWithInt:*lang]] forKey:#"lang"];
}
// does this work? is a mutable dictionary a subclass of dictionary?
return dictionary;
}
It looks a bit like you've tried to translate the Java code line by line, which won't lead to anything but a headache. A number of things I'd like to point out:
Initialiser methods should always return type id (not a superclass).
Initialiser methods that do nothing but return self don't need to be implemented, this is because if no init method is provided for the class, it will go straight to invoking the superclass's init method.
The memory management for your proxyForJson method is wrong. You are returning an object that you own through a method whose name implies no ownership of the returned value. Instead, create the dictionary by using [NSMutableDictionary dictionary] as this result of this method has no owner.
Your class uses global variables that probably shouldn't be global. I'm not entirely sure, but I think it would be better to make lang an instance variable of your class that is set via an argument to an init method, e.g. initWithLanguageID:. Certainly this would be closer to your Java implementation.
Since enums are sequential and start from 0 (unless specifically written otherwise), you could probably avoid using a map entirely and use a simple static array of NSString objects, one for each entry of the Lang enumeration. Better yet, don't use an enum and allow the caller to specify the language as a string. From what I can see, the enumeration doesn't seem to have any other use but to map an integer to a string.