Struct isKindOfClass - ios

How do I find out if a struct is of a specific type? In other words, if I get an object, how do I know that the underluying type is a struct?
+(BOOL)isPrimitive:(id)input
{
return [input isKindOfClass:[NSNumber class] ] || [input isKindOfClass:[NSDate class]] || [input isKindOfClass:[NSString class]]
|| __IS_THIS_A_STRUCT__ (specifically SEL);
}
What should I put in place of IS_THIS_A_STRUCT?

Based on your comments, it looks like you know a property and want to act in a certain way if it returns a struct. If so then you could do something like:
if(!strcmp([[self class]
instanceMethodSignatureForSelector:#selector(propertyName)].methodReturnType,
#encode(SEL)))
#encode returns the type encoding for the named type, which is a C string. instanceMethodSignatureForSelector returns an NSMethodSignature which can nominate the return type of that method as an encoded type.
The two type encodings are not guaranteed to have the same identity but will have the same value. So you can use the C function strcmp to check that they're the same.
You can use NSSelectorFromString if the selector name is not known at compile time.

Related

iOS: Is this a good way to check if JSON dictionary object is an NSString?

I want to check if a JSON object is an NSString and if it isn't, assign it a default string. My ultimate goal is to prevent crashing and assign the properties a proper value no matter what. This is an example of a data model I am using where dict is the JSON dictionary the API returns.
Data *data = [[self alloc] init];
data.name = [NSString validateString:dict[#"name"] defaultString:#""];
data.status = [NSString validateString:dict[#"status"] defaultString:#"OPEN"];
Here is the category method validateString I am using.
+ (NSString *)validateString:(NSString *)aString defaultString:(NSString *)defaultString {
if ([aString isKindOfClass:[NSString class]]) {
return aString;
}
return defaultString;
}
It makes no sense, and is very bad practice, to cast (NSString *)aString and then ask if this is in fact an NSString.
Also, what if it is nil?
All you know when you fetch from a dictionary is that you get an id. Do not assume more than that.
I would suggest writing very plainly: say what you mean, and mean what you say. That is the best practice in Objective-C. Otherwise, dynamic typing and "nil trickery" can lead you into subtle errors. You might not have any trouble in this particular case, but bad habits are bad habits, and it is best not to let them form in the first place. I'd rewrite like this:
+ (NSString *) checkType:(nullable id)obj defaultString:(NSString *)def {
if (obj == nil || ![obj isKindOfClass:[NSString class]]) {
return def;
}
return obj;
}
Like mentioned in other comments: if you want to prevent crashes, you also need to check if it's nil, specially if there is a chance to port your code to Swift in the future.
Just to clarify my last sentence, the line below works in Objective-C even if aString is nil:
if ([aString isKindOfClass:[NSString class]]) {
That's because, in the way Objective-C was made, calling a function on a nil object returns nil, so the if will be considered false, and the function will return defaultString. Yeah... that's certainly a bad idea when they created Objetive-C, since this leads to lots of errors. More details about that behaviour below:
https://stackoverflow.com/a/2696909
Anyway, it's also a good practice to only cast an object after checking its type, so I would recommend adapting your function to this:
+ (NSString *)validateString:(id)obj defaultString:(NSString *)defaultString {
if (obj != nil && [obj isKindOfClass:[NSString class]]) {
return (NSString*)obj;
}
return defaultString;
}
Every object that implements NSObject* has isKindOfClass: (and NSDictionary* only stores objects that implement NSObject*), so we don't need to check if the object responds to it. Also, even if we wanted, respondsToSelector: is also an NSObject* function.
Still, the method that you are using still works. The revised function above is just adapted to better practices and to avoid problems in case you ever need to port this code to Swift (or any other language) in the future.
EDIT: Updated code based in #matt's suggestion.

ObjC: differentiating between NSValue and NSNumber

I have some code where I will be receiving an object of unknown type. It could be a NSString, NSNumber, a scalar wrapped in a NSValue or some other class:
-(void) doSomethingWith:(id) value {
if ( <test-for-NSValue> ) {
// Do something with a NSValue
} else {
// Do something else
}
}
I need to identify where there is a scalar type inside a NSValue.
The problem is identifying a NSValue wrapped scalar vs a NSNumber. As NSNumber inherits from NSValue and both are class clusters, I'm haing trouble sorting them out.
So:
[value isKindOfClass:[NSValue class]] ... sees NSNumbers as NSValues.
[value isMemberOfClass:[NSValue class]] ... doesn't recognise NSValues because the instances are concrete subtypes.
Anyone got any idea how to do this?
first we need about the diffrenciation between iskindofClass and isMemberOfClass
isKindOfClass
Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
YES if the receiver is an instance of aClass or an instance of any class that inherits from aClass, otherwise NO.
isMemberOfClass
Returns a Boolean value that indicates whether the receiver is an instance of a given class.
YES if the receiver is an instance of aClass, otherwise NO.
Then very importantly
NSValue
An NSValue object is a simple container for a single C or Objective-C data item. It can hold any of the scalar types such as int, float, and char, as well as pointers, structures, and object id references. Use this class to work with such data types in collections (such as NSArray and NSSet), key-value coding, and other APIs that require Objective-C objects. NSValue objects are always immutable.
NSNumber
NSNumber is a subclass of NSValue that offers a value as any C scalar (numeric) type. It defines a set of methods specifically for setting and accessing the value as a signed or unsigned char, short int, int, long int, long long int, float, or double or as a BOOL. (Note that number objects do not necessarily preserve the type they are created with.) It also defines a compare: method to determine the ordering of two NSNumber objects
if ([value isKindOfClass:[NSValue class]]) //It will return YES because NSNumber value subclass or inherits from NSValue
{
..........
}
if ([value isMemberOfClass:[NSValue class]]) //It will return NO because NSNumber value is not a member of the NSValue
{
.........
}
Class objects may be compiler-created objects but they still support the concept of membership. Thus, you can use this method to verify that the receiver is a specific Class object.
What about:
-(void) doSomethingWith:(id) value {
if ([value isKindOfClass:[NSValue class]] && ![value isKindOfClass:[NSNumber class]]) {
// NSValue but not instance of NSNumber
} else {
...
}
}

Customized NSLog to print all type of objects

I'm trying to extend NSLog that can print any type of object. Means, my custom class gets the value that need to be printed from the user and checks its class type and then it will print as desired format.
Consider my extended class name is, MyLog (So it contains MyLog.h and MyLog.m)
MyLog.h:
void MyLog (id objectValue);
MyLog.m:
#import "MyLog.h"
void MyLog (id objectValue)
{
if ([objectValue isKindOfClass:[NSString class]])
{
NSLog(#"%#",objectValue); //Same for NSArray, NSDictionary, NSMutableArray, NSMutableDictionary
}
else if ([objectValue isKindOfClass:[NSData class]])
{
NSLog(#"%#",[[NSString alloc]initWithData:objectValue encoding:NSUTF8StringEncoding]);
}
....
....
}
So, if I include this class (MyLog.h) in prefix file, I can call the below method from any class to simply print the given object.
MyLog(valueOfObject);
Problems:
The CGRect, CGpoint, CGSize related things (which are not an object) can not be passed as id to MyLog() function.
I tried to print the object name along with its value for more readability. Refer this post.
For example,
NSString *myString = #"Welcome";
MyLog(myString);
This will print myString: Welcome instead of just printing Welcome.
I can achieve this my defining a preprocessor like
#define MYLOG(x) NSLog( #"%s:%#",#x, x)
So, I tried to customize it in the following way
#define MYLOG(x) NSLog(#"%s:%#",#x, MyLog(x))
But it throws "Argument type 'void' is incomplete" error.
Questions:
How can I pass non objects as id to a function?
How can I call a function within a preprocessor macro?
Help needed!! Just Confused!!
If you implement -(NSString*)description you can print any object in NSLog with NSLog(#"%#", myObject).
So just add -(NSString*)description in the classes of objects that you want to print out, and in them just return the NSString value that you think is relevant for that object.

#encoding for type id where id is actually any object

I have a following method.
- (void)someObject:(id)obj {
char* encoding = #encoding(typeof(obj));
NSString *s = [NSString stringWithCString:encoding encoding:NSUTF8StringEncoding];
NSLog(s);
}
this method always return #"#" whether I pass a variable of type NSNumber, NSArray, NSDictionary or NSString in obj. I assume it is checking the obj pointer type.
What do I have to do so that it returns the actual type encoding of the variable that I have passed?
#encode() is a compile-time construct; it only knows about the variable's type, not the class of the object that will be contained in the object at runtime. Any object-typed variable will encode to #.
You will have to use runtime checks, asking the objects for their classes, to accomplish your goal.

Case insensitive compare against bunch of strings

What would be the best method to compare an NSString to a bunch of other strings case insensitive? If it is one of the strings then the method should return YES, otherwise NO.
Here's a little helper function:
BOOL isContainedIn(NSArray* bunchOfStrings, NSString* stringToCheck)
{
for (NSString* string in bunchOfStrings) {
if ([string caseInsensitiveCompare:stringToCheck] == NSOrderedSame)
return YES;
}
return NO;
}
Of course this could be greatly optimized for different use cases.
If, for example, you make a lot of checks against a constant bunchOfStrings you could use an NSSet to hold lower case versions of the strings and use containsObject::
BOOL isContainedIn(NSSet* bunchOfLowercaseStrings, NSString* stringToCheck)
{
return [bunchOfLowercaseStrings containsObject:[stringToCheck lowercaseString]];
}
Just to add a few additions to Nikolai's answer:
NSOrderedSame is defined as 0
typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};
So if you call caseInsensitiveCompare: on a nil object you would get nil. Then you compare nil with NSOrderSame (which is 0) you would get a match which of course is wrong.
Also you will have to check if parameter passed to caseInsensitiveCompare: has to be not nil. From the documentation:
This value must not be nil. If this value is nil, the behavior is
undefined and may change in future versions of OS X.

Resources