Protocol subclassing and conformance - ios

Given
#protocol Response <NSObject>
#end
#protocol DataResponse <Response>
#end
#interface ListUsersResponse : NSObject <DataResponse>
#end
#interface RequestExecutor : NSObject
+(id<Response>) execute: (id<Request>) request receptor: (Class) model;
#end
ListUsersRequest * request = [self buildListUsersRequest];
ListUsersResponse * result = [RequestExecutor execute:request receptor:[ListUsersResponse class]];
I get an Initializing 'ListUsersResponse *__strong' with an expression of incompatible type 'id<Response>' error. Why is that? Cant the compiler detect protocol conformance? How to solve it?

These warning is because your RequestExecutor's execute: method returns an object of a general id<Response> type. The variables, however, are declared as ListUsersResponse * so the compiler expects a more specific type (and it isn't sure if that type cast is correct or not, it has no way of knowing).
You can get rid of the warning by either:
a) Declaring your variables with id<Response> type instead of ListUsersRequest * type like:
id<Response> result = [RequestExecutor execute:request receptor:[ListUsersResponse class]];
Or
b) Casting them on the fly (if you are sure that at that point they will be of the appropriate class):
ListUsersResponse *result = (ListUsersRequest *)[RequestExecutor execute:request receptor:[ListUsersResponse class]];

Related

Sending 'double' to parameter of incompatible type 'id _Nullable'

So I am unable to understand this error.
I have an object with these properties
#property (nonatomic, readwrite) double width;
#property (nonatomic, readwrite) double height;
When I am creating an NSSdictionary add adding them into it
if (config.width) {
properties[#"height"] = config.height;
}
if (config.height) {
properties[#"height"] = config.height;
}
I am getting following error
Sending 'double' to parameter of incompatible type 'id _Nullable'
If I am correct, type id is equivalent to any? which includes double. Also, I am setting value if it exist?
If I am correct, type id is equivalent to any? which includes double
But you're not correct. id is equivalent to any object (an NSObject subclass). double is not an object; it's a primitive.
Just in case if anyone wants to know how I was able to fix it, I did
[NSNumber numberWithDouble:config.height];

Serialize JSONModel - How to convert custom object to JSON in iOS?

How to serialize a custom object with JSONModel ?
#property(nonatomic,strong) NSString<Optional> * tag_post_type;
#property(nonatomic,strong) NSString<Optional> * tag_users_type;
#property(nonatomic,strong) NSArray<Optional> * tags;
#property(nonatomic,strong) NSMutableArray<TagMediaModel*>* tag_posts;
#property(nonatomic,strong) NSMutableArray<TagLocationModel*>* locations;
I try to create a JSON file out of my custom Object with the JSONModel framework for iOS. I get the Error:
EXCEPTION: Invalid type in JSON write (TagMediaModel)
When I call toJSONString Method, I got this issue.
[tagAutomaticModel toJSONString];
This is the model data:
locations = (
"<TagLocationModel> \n [id]: 780307344\n [name]: Hotel Central Park, india\n</TagLocationModel>",
"<TagLocationModel> \n [id]: 463004401\n [name]: Miraj Cinema new year\n</TagLocationModel>",
"<TagLocationModel> \n [id]: 246187965\n [name]: Surya Treasure Island asia\n</TagLocationModel>",
);
"tag_posts" = (
"<TagMediaModel> \n [media_code]: BS0tQeFhU_Z\n [media_id]: 1492016420475981785\n [media_url]: https://scontent.cdninstagram.com/t51.2885-15/e15/17881459_...\n [media_type]: I\n</TagMediaModel>"
);
#property(nonatomic,strong) NSMutableArray<TagMediaModel>* tag_posts;
made few change as above, just removed the star in TagMediaModel.
The angle brackets after NSMutableArray contain a protocol. As #R.Mohan said, you need to remove * pointer and use without pointer.
The protocol must be in place. So add #protocol tag_posts and #protocol locations at top in respective class.
EXCEPTION: Invalid type in JSON write (TagMediaModel) I guess you are converting json to your custom class one or more times. Do it only once. It will resolve your problem.

#import "Project-Swift.h" comes some strange issue

Project is mixed with oc and swift.
#import "myProjectName-Swift.h" in the AppDelegate.m,:
Project is Objective-c language created by me, and using almost swift language to write. and In the Appdelegate.m I want to use swift's class, and so
But unfortunate, my project occurs some error in myProjectName-Swift.h:
The code is below:
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, copy)
NSString * _Nonnull ;)
+ (NSString * _Nonnull);
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, copy)
NSString * _Nonnull ;)
+ (NSString * _Nonnull);
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, strong) UIColor * _Nonnull APP_COLOR;)
+ (UIColor * _Nonnull)APP_COLOR;
The error are:
Expected ';' at end of declaration list.
cannot declare variable inside #interface or #protocal
Expected member name or ';' after declaration specifiers
Expected ']'
Property requires fields to be named
Missing '[' at start of message send expression
And so on...
Attension
And In the global swift, I set Chinese characters variable like this:
class Global: NSObject {
}
EDIT - 1
Because I think a error point out the static let "'s ,so I annotation this line, project will not show this error. Or I delete the ,`, will not show the error too.
Objective-C doesn't support Unicode in identifiers (variable names, class names, method names etc), only in string literals ("🏋"). Only a subset of ASCII is allowed (start with _[a-zA-Z], +[0-9] afterwards).
Example:
class MyClass : NSObject {
func 😞() { }
}
This will break when you compile it in a mixed project. Instead you need to give it a valid Objective-C name, like so:
class MyClass : NSObject {
#objc(unhappyFace)
func 😞() { }
}
which will compile (and you need to access it as unhappyFace on the ObjC side).
Or in your example you need to modify your Global object:
class Global: NSObject {
#objc(emptyNumberNotAllowed)
static let 手机号码不能为空 = "手机号码不能为空"
... etc ...
}

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.

Resources