Objective-C how to define static parameters - ios

I'm new at Objective-C. I want to use global parameters like in C/C++
#define PARAM_1 1
How could I do this in Objective-C

The same way.
Objective-C is a superset of C, so your define is perfectly valid.
edited following comment
Rather than a define, which just performs textual substitution, you could use a static variable instead:
static NSNumber const * retrieveFriendRequestNumber = nil;
Which you can initialise in the class's initialiser method
+ (void)initialize {
retrieveFriendRequestNumber = #(2);
}
(Yes a bit long-winded, but an example of how to initialise a literal).
and then you can use it as:
[parameters setObject:retrieveFriendRequestNumber forKey:#"fcode"];
Alternatively, declare it as a static NSUInteger and convert it to an object when you use it:
static NSUInteger retrieveFriendRequest = 2;
And use it as:
[parameters setObject:#(retrieveFriendRequest) forKey:#"fcode"];

Related

iOS invoke class method with IMP

Say I am developing a tweak app, I want to create an instance of one Class whose header can't be imported, but I do know class name, class methods and instance methods How can I create it in class method with parameters?
Say this Class called MMClass with class method
+(instancetype)do:(NSString*)string for:(NSString *)antherString;
What I am doing is as below:
Class class = objc_getClass("MMClass");
Method initMethod = class_getClassMethod(class,
#selector(do:for:));
IMP imp = method_getImplementation(initMethod);
id instance = imp(class,#selector(do:for:),#"do",#"ye");
Is this right?
First, I'm not sure if I'm stating the obvious, but why not just create your own header with the declarations you want to use and import that (or just inline the declaration at the top of your file if you are only going to use it in one file)? and call the methods normally? instead of going through all this mess? All the compiler cares about is that it sees some declaration for the methods you want to call.
When you call the actual method implementation using a function pointer, you need to cast it to the right type of function pointer corresponding to the signature of the method:
Class class = objc_getClass("MMClass");
Method initMethod = class_getClassMethod(class, #selector(do:for:));
IMP imp = method_getImplementation(initMethod);
id (*foo)(Class, SEL, NSString *, NSString *) =
(id (*)(Class, SEL, NSString *, NSString *))imp;
id instance = foo(class, #selector(do:for:), #"do", #"ye");
But it's silly to get an IMP that you are only going to use once. Rather, you should just cast objc_msgSend to the desired function pointer type, and call that directly instead:
Class class = objc_getClass("MMClass");
id (*foo)(Class, SEL, NSString *, NSString *) =
(id (*)(Class, SEL, NSString *, NSString *))objc_msgSend;
id instance = foo(class, #selector(do:for:), #"do", #"ye");

Is there a way to Make NSInvocation surport variable parmas function line [NSstring stringWithFormat:..]

Apple doc says "NSInvocation does not support invocations of methods with either variable numbers of arguments or union arguments. "
i searched for hours ,some people says var_list works, but i tryed ,it does Not
but I think there may be a way to do the same thing (reflection) on variable params function,as i metioned ,[stringWithFormat:],
so , I found a way ,please readt the code bellow .
#interface INTObj : NSObject
#property (nonatomic, assign) int realvalue
#end
#interface FloatObj : NSObject
#property (nonatomic, assign) float realvalue
#end
// here ,the selectorName is only know in runtime
NSString *selectorName = #"stringWithFormat:";
SEL selector = NSSelectorFromString(selectorName);
typedef id (*Obj_Imp)(id,SEL,...);
Method md = class_getClassMethod(obj, selector); // here, obj means NSString
Obj_Imp fun = (Obj_Imp )method_getImplementation(md); // stringWithFormat:...
NSString *arg1 = #"hello %f %d";
FloatObj *fo = [[FloatObj alloc] init];
fo.realvalue = 11.3;
INTObj *io = [[INTObj alloc] init];
io.realvalue = 5;
NSObject *arr[3] = {arg1, fo,io};
// it cracks . exc_bad_Access code = 1
NSString *result = fun(obj , selector, arr[0], [arr[1] realvalue],[arr[2] realvalue]) ;
// it works but i cant do this ,because i don't know the type of the 4th parameters at Runtime
NSString *result = fun(obj , selector, arr[0],[(FloatObj *)arr[1] realvalue],[arr[2] realvalue])
why does the second calling of the function "fun" works while the first one cracks?
is there a better way to to do this?
This has nothing to do with NSInvocation or calling the method implementation directly. You should get the same undefined behavior if you called the method directly:
NSString *result = [NSString stringWithFormat:arr[0], [arr[1] realvalue], [arr[2] realvalue]];
or even a regular function:
NSLog(arr[0], [arr[1] realvalue], [arr[2] realvalue]);
In C (and Objective-C) every expression must have a compile-time type. The compiler needs to be know this compile-time type be able to compile the code correctly.
So let me ask you, what should be the compile-time type of [arr[1] realvalue]? Is it int? Is it float? The compiler will do different things depending on what type it is. If it is float for example, the C standard says that float passed to varargs will be promoted to double. The calling conventions for passing an int and a double in varargs are different (in fact, these two types have different sizes). And +[NSString stringWithFormat:] expects the compile-time type of the thing you pass to match the format specifier you give it in your format string, or there will be undefined behavior.
From your format string, it seems like you wanted the [arr[1] realvalue] argument to be float or double since you used %f. Since FloatObj is the class whose realvalue method returns float, it seems that casting arr[1] to FloatObj * is the right thing to do.

How to JSExport a Objective-C Method with Object

I want to write a method by the Objective-C that can be exported to JavaScript, so that I can get the JavaScript Object into the native code. For example in the following code: someObject is a native implemented object, and fun is its method.
someObject.fun({ k : 'value1', k2 : 'value2'})
I know that using JSExport can export the native method to JavaScript. and successfully pass the JavaScript String to the native code (NSString*). But when I want to pass the JavaScript Object to native, it fails.
How to solve this?
Thanks a lot
In addition to the conversion of javascript strings to NSString* as you have already observed, Javascript objects are copied to a KVC-compliant objective-C object (essentially, an NSDictionary instance) before being passed to your objective-c method.
For example, with the following definitions:
#protocol MyJSAPI
- (void)fun:(id)param;
#end
#interface MyObject: NSObject<MyJSAPI>
- (void)fun:(id)param;
#end
and the initialization of your JSContext as follows:
myjscontext[#"someObject"] = [[MyObject alloc] init];
[myjscontext evaluateScript:#"someObject.fun({ k : 'value1', k2 : 'value2'});
then your fun:(id)param method can access the k and k2 fields of the objects like this:
#implementation MyObject
- (void)fun:(id)param {
NSString* k = [param valueForKey:#"k"];
NSString* k2 = [param valueForKey:#"k2"];
NSLog(#"k = %#, k2 = %#", k, k2);
}
#end
In addition to ste3veh answer.
You can use JSExportAs macro in following way to export Objective-C method as JavaScript one:
#interface MyObject: NSObject <JSExport>
JSExportAs(fun,
- (void)fun:(id)param
);
#end
#implementation MyObject
- (void)fun:(id)param {
NSString* k = [param objectForKey:#"k"];
NSString* k2 = [param objectForKey:#"k2"];
NSLog(#"k = %#, k2 = %#", k, k2);
}
#end
In this case param would be implicitly converted to corresponding Objective-C oblect, using -[JSValue toObject]. In your case it will be converted in NSDictionary. To avoid implicit conversion, you may specify parameter as JSValue:
#interface MyObject: NSObject <JSExport>
JSExportAs(fun,
- (void)fun:(JSValue*)param
);
#end
#implementation MyObject
- (void)fun:(JSValue*)param {
NSString* k = [[param valueForProperty:#"k"] toString];
NSString* k2 = [[param valueForProperty:#"k2"] toString];
NSLog(#"k = %#, k2 = %#", k, k2);
}
#end
Note that big JavaScript objects, i.e. window or document, are very expensive to convert into corresponding Objective-C ones, so second way is preferred. Also, you can use JSValue parameter to call back JavaScript methods of this object from Objective-C, using callWithArguments:.
I don't think you can. See the JSExport.h header: It defines what the arguments can be, and it doesn't appear that generic JavaScript objects can be passed.

NSArray redefinition with a different type int & missing type specifier

The following code will not build and provides the warning of "Type specifier missing, defaults to 'int'" and the error "Redefinition of 'my_var' with a different type: 'int' vs 'NS Array *__strong'.
NSArray *my_var = nil;
my_var = #[
#[#312, #"Name1"],
#[#313, #"Name2"]
];
What am I missing? I've tried many different refactorings and for some reason cannot compile with a pre-defined NSArray.
You've put that code at global or file scope, outside of any function or method definition. You can't do that. The second assignment to my_var is only valid inside a function or method body. Outside of a function/method body, the compiler thinks the second line is another variable declaration, with a (default) type of int.
You can't initialize my_var to an NSArray literal statically. A NSArray literal is different than a literal NSString. A literal NSString is created by the compiler and stored, fully usable, in the executable file. An NSArray literal turns into code that calls methods at runtime (under the covers) to create the array. That code is only allowed inside a function or method body.
If you want a global constant NSArray, use a global function that creates the array once and returns it every time. Declare it in a .h file like this:
NSArray *my_var();
And define it in a .m file like this:
NSArray *my_var() {
static dispatch_once_t once;
static NSArray *array;
dispatch_once(&once, ^{
array = #[
#[#312, #"Name1"],
#[#313, #"Name2"]
];
});
return array;
}
It looks to me like you want the objects of your array to be dictionary objects:
So why not try:
NSArray *my_var = #[
#{ #"Name1" : #312 },
#{ #"Name2" : #313 },
];

How do you return a struct like CLLocationCoordinate2D from a class method properly?

This question uses CLLocationCoordinate2D as an example, but this applies to other structs as well, such as CGPoint (although ones like those are usually automatically included).
I want to use CLLocationCoordinate2D as a return value in a class method. If it were an object you could write the following at the top and it would be fine, as long as the .m file had a reference to CoreLocation.h
#class ClassName
Is there an equivalent way of telling the compiler not to worry about the struct without re-declaring it or importing the header file into the class' header file?
I do not want to import CoreLocation.h into the header file, since that would mean every file that imports that header file would inherit CoreLocation.
Thanks
I'm not totally getting the point why you do not want to import CoreLocation, but CLLocationCoordinate2D is declared in CoreLocation.h. I'm not aware about a method like #class for struct and I don't think it exists since struct are C types.
What you can do is create your own class that wraps the CLLocationCoordinate2D or return the NSValue from it, or (why not?) a dictionary.
Easiest way to do this is to just use an object instead of the struct, then you can use the #class keyword. In this case, the CLLocation object works just fine. Alternatively you can often use an NSDictionary in place of a struct, but an object is a bit easier to manage.
You return a struct like any other type. But you should be aware that when returning a struct you are returning a copy of the internal value on the stack as a temporary variable. Unlike an Objective-C object where you are actually returning a pointer.
The type you return MUST be a complete type. That means, in your method declaration you need the definition of the struct. In your case, that means, you need to include the header.
For example:
typedef struct MyStruct {
int a;
int b;
} MyStruct;
#interface MyClass : NSObject
+(MyStruct) theStruct;
#end
#implementation MyClass
+(MyStruct) theStruct {
MyStruct s = {.a = 1, .b = 2};
return s;
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
MyStruct s1 = [MyClass theStruct];
s1.a = 100;
s1.b = 100;
NSLog(#"s1 = {%d, %d}", s1.a, s1.b);
NSLog(#"MyStruct.theStruct = {%d, %d}", [MyClass theStruct].a, [MyClass theStruct].b);
[MyClass theStruct].a = 0; // has no effect!
}
return 0;
}
Prints:
s1 = {100, 100}
MyStruct.theStruct = {1, 2}
There is no straightforward way of doing that with single keyword.
Here you can find why it is not straightforward, although it is stated that it is not possible to do that, somewhat true but not completely.
Forward declare a struct in Objective-C
And here is the workaround of doing this
Forward declare structs in Objective C
Hope this will help.

Resources