Objective-C Safe Casting Macro - objective-c

I've written a macro in Objective-C to perform a safe cast. Here's what it looks like so far:
#define SAFE_CAST(OBJECT, TYPE) ([OBJECT isKindOfClass:[TYPE class]] ? (TYPE *) OBJECT: nil)
This works really well, but it'd be nice if there was a way to store OBJECT in a variable so it didn't get called twice. For instance, using the macro as such:
NSString *str = SAFE_CAST([dictinary objectForKey:key], NSString);
results in code similar to this when the macro is expanded:
NSString *str = ([[dictinary objectForKey:key] isKindOfClass:[NSString class]] ? (NSString *) [dictinary objectForKey:key]: nil);
I'd prefer for it to work more like this:
id obj = [dictionary objectForKey:key];
NSString *str = ([obj objectForKey:key] isKindOfClass[NSString class]] ? (NSString *) obj : nil);
Thanks.

You can use a GCC extension called statement statement expressions to have
#define SAFE_CAST(OBJECT, TYPE) ({ id obj=OBJECT;[obj isKindOfClass:[TYPE class]] ? (TYPE *) obj: nil; })
That said, I think it's generally a bad approach to have a situation where you need to use SAFE_CAST a lot.
Never put objects of different classes in an array; never reuse an action message (IBAction)someAction:(id)sender for UI objects of different classes. Then you usually don't need to use SAFE_CAST.

If you really think you must do this, you could use a function:
#define SAFE_CAST(Object, Type) (Type *)cast_helper(Object, [Type class])
static id cast_helper(id x, Class c) {
return [x isKindOfClass:c] ? x : nil;
}

So write it like that, just wrap it in do { } while(0) <-- and not just in parenthesis.
#define SAFE_CAST(OBJECT, TYPE, VAR) do { \
id obj = OBJECT; \
VAR = [obj isKindOfClass:[TYPE class]] ? (TYPE *) obj : nil; \
} while(0)

Related

Swift's "if let" equivalent in Objective C

What would be "if let" equivalent in Objective C? The example snippet I want to convert to Objective C is below;
if let pfobjects = images as? [PFObject] {
if pfobjects.count > 0 {
var imageView: PFImageView = PFImageView()
imageView.file = pfobjects[0] as! PFFile
imageView.loadInBackground()
}
}
There's no direct equivalent to if let in Objective-C, because if let does Swift-specific things (unwrapping optionals and rebinding identifiers) that don't have direct equivalents in Objective-C.
Here's a nearly equivalent of your Swift code:
if (images != nil) {
NSArray<PFObject *> *pfobjects = (id)images;
if (pfobjects.count > 0) {
PFImageView *imageView = [[PFImageView alloc] init];
assert([pfobjects[0] isKindOfClass:[PFFile class]]);
imageView.file = (PFFile *)pfobjects[0];
[imageView loadInBackground];
}
}
But this Objective-C code won't verify that images only contains instances of PFObject, and should successfully create an image view as long as pfobjects[0] is a PFFile. Your Swift code will do nothing (create no image view) if images contains any non-PFObject elements.
You can use NSPredicate to verify the array contains only instances of PFObject:
NSPredicate *p = [NSPredicate predicateWithFormat:#"self isKindOfClass: %#", [PFObject class]];
NSInteger numberThatArePFObjects = [images filteredArrayUsingPredicate:p].count;
if(numberThatArePFObjects && numberThatArePFObjects == images.count){
// certain that images only contains instances of PFObject.
}
If however you weren't working with an array but a single object then it is simpler:
if([image isKindOfClass:[PFObject class]]){
// certain that image is a valid PFObject.
}
Or if you wanted a new variable:
PFObject* obj = nil;
if([image isKindOfClass:[PFObject class]] && (obj = image)){
// certain that obj is a valid PFObject.
}
You can use something like this:
NSArray<PFObject *> *pfobjects;
if ([images isKindOfClass: [NSArray<PFObject> class]] && (pfobjects = images)) {
// your code here
}
You want three things simultaneously. Let's split them:
variable as? OtherType is possible, but erases type, because it returns id. Implementation is as easy as a category on NSObject class, so it becomes NSArray *array = [jsonDict[#"objects"] ifKindOfClass:NSArray.class].
Implementation
#interface NSObject (OptionalDowncast)
- (id)ifKindOfClass:(__unsafe_unretained Class)clazz;
#end
#implementation NSObject (OptionalDowncast)
- (id)ifKindOfClass:(__unsafe_unretained Class)clazz {
return [self isKindOfClass:clazz] ? self : nil;
}
#end
if let is also possible in Objective-C if type is known, so it cannot be combined with previous thing. Easiest way is: for(NSArray *array = [self getItems]; array != nil; array = nil) { ... }, but if you want to use else branch, it gets a bit more complex. I have made SwiftyObjC pod for that, please take a look
Check generic template is not possible during type cast in Objective-C, thus you can cast to NSArray, but you can't cast to NSArray<PFObject>
I don't see iterations over your array: With all that being said, I think best example is (assuming images is an array already):
for(PFFile *file = [images.firstObject ifKindOfClass:PFFile.class]; file != nil; file = nil) {
imageView.file = file;
[imageView loadInBackground];
}
If you need to also iterate over it:
for(id object in images) {
for(PFFile *file = [object ifKindOfClass:PFFile.class]; file != nil; file = nil) {
//operate on file
}
}
You can use Objective-C++ in place of Objective-C. In this you can use the next define:
#define let const auto
Note: it is not the same exactly (Swift has wrapped values, ...) but it makes the work easier.
And through of this define you can use it of this way:
if (let pfobjects = images) {
if (pfobjects.count > 0 {
let imageView = [[PFImageView alloc] init];
imageView.file = pfobjects[0];
imageView loadInBackground();
}
}
To convert your Objective-C class in Objective-C++ class you only must change the extension of implementation file of .m to .mm

How to check a typedef'd obj in Objective-c NSDictionary

I've got an method that takes NSDictionary arg. This NSDictionary has some predefined keys it'll take. All the obj's should be strings. But only certain string objs are valid for each key.
So my approach was to typedef NSString for each valid string per key. I'm hoping not to extend the NSString class.
I've typedef'd some NSString's...
typedef NSString MyStringType
Then I define a few...
MyStringType * const ValidString = #"aValidString";
Here's what I'd like to do in my sample method..
- (void)setAttrbiutes:(NSDictionary *)attributes {
NSArray *keys = [attributes allKeys];
for (NSString *key in keys) {
if ([key isEqualToString:#"ValidKey"]) {
id obj = [attributes objectForKey:key];
//Here's where I'd like to check..
if (**obj is MyStringType**) {
}
}
}
}
I'm open to other ideas if there's a better approach to solve the obj type problem of an NSDictionary.
Doesn't work like that; typedefs are a compile time alias that don't survive being passed through a dictionary.
In any case, using typedefs for something like this would be unwieldy.
I suggest you create a property list -- either as a file in your project or in code -- that contains the specifications of your various keys and valid values, then write a little validator that, passed a string and value, can validate the string-value pair for validity.
This also gives you the flexibility to extend your validator in the future. For example, you might have a #"Duration" key that can only be in the range of 1 to 20.
Instead of setting up a typedef for you special values, one possible option would be to create an NSSet of the special values. Then in your code you can verify that the object in the dictionary is in your set.
What about a combination of category on NSString + associated object?
Something along the lines (untested!!):
#interface NSString (BBumSpecial)
- (NSString *) setSpecial: (BOOL) special ;
- (BOOL) special ;
#end
and:
#implementation NSString (BBumSpecial)
static void * key ;
- (NSString *) setSpecial: (BOOL) special {
objc_setAssociatedObject(self, &key, special ? #YES : #NO, OBJC_ASSOCIATION_ASSIGN) ;
return self ;
}
- (BOOL) special {
id obj = objc_getAssociatedObject(self, &key) ;
return obj && [obj boolValue] ;
}
#end
Which you could then use as:
NSString * mySpecialString = [#"I'm Special" setSpecial:YES] ;
?

Objective c implement method which takes array of arguments

Hee
Does anybody know how to implement an method in objective c that will take an array of arguments as parameter such as:
[NSArray arrayWithObjects:#"A",#"B",nil];
The method declaration for this method is:
+ (id)arrayWithObjects:(id)firstObj...
I can't seem to make such method on my own. I did the following:
+ (void) doSometing:(id)string manyTimes:(NSInteger)numberOfTimes;
[SomeClass doSometing:#"A",#"B",nil manyTimes:2];
It will give the warningtoo many arguments to function 'doSometing:manyTimes:'
Thanks already.
The ellipsis (...) is inherited from C; you can use it only as the final argument in a call (and you've missed out the relevant comma in your example). So in your case you'd probably want:
+ (void)doSomethingToObjects:(id)firstObject, ...;
or, if you want the count to be explicit and can think of a way of phrasing it well:
+ (void)doManyTimes:(NSInteger)numberOfTimes somethingToObjects:(id)firstObject, ...;
You can then use the normal C methods for dealing with ellipses, which reside in stdarg.h. There's a quick documentation of those here, example usage would be:
+ (void)doSomethingToObjects:(id)firstObject, ...
{
id object;
va_list argumentList;
va_start(argumentList, firstObject);
object = firstObject;
while(1)
{
if(!object) break; // we're using 'nil' as a list terminator
[self doSomethingToObject:object];
object = va_arg(argumentList, id);
}
va_end(argumentList);
}
EDIT: additions, in response to comments. You can't pass the various things handed to you in an ellipsis to another function that takes an ellipsis due to the way that C handles function calling (which is inherited by Objective-C, albeit not obviously so). Instead you tend to pass the va_list. E.g.
+ (NSString *)doThis:(SEL)selector makeStringOfThat:(NSString *)format, ...
{
// do this
[self performSelector:selector];
// make string of that...
// get the argument list
va_list argumentList;
va_start(argumentList, format);
// pass it verbatim to a suitable method provided by NSString
NSString *string = [[NSString alloc] initWithFormat:format arguments:argumentList];
// clean up
va_end(argumentList);
// and return, as per the synthetic example
return [string autorelease];
}
Multiple arguments (also known as an arglist) can only come at the end of a method declaration. Your doSomething method would look something like this:
+ (void)doNumberOfTimes:(NSInteger)numberOfTimes withStrings:(id)firstArg, ...
{
va_list args;
va_start(args, firstArg);
NSString * argString = firstArg;
while (argString != nil)
{
// do something with argString here
argString = va_arg(args, NSString *);
}
va_end(args);
}
To be called as follows:
[SomeClass doNumberOfTimes:2 withStrings:#"A", #"B", nil];
See also: How to create variable argument methods in Objective-C
I think you're after a variadic function. Here's Apple's documentation: http://developer.apple.com/library/mac/qa/qa2005/qa1405.html

Anyway to get string from variable name?

Say I have my class
#interface Person : NSObject { NSString *name; }
I need to get the name of NSString's within my class
Person *person = [[Person alloc] init];
NSLog(#"Name of variable %s\n", _NameofVariable_(person->name));
Thanks for the answers, here's the solution I came up from the replies
//returns nil if property is not found
-(NSString *)propertyName:(id)property {
unsigned int numIvars = 0;
NSString *key=nil;
Ivar * ivars = class_copyIvarList([self class], &numIvars);
for(int i = 0; i < numIvars; i++) {
Ivar thisIvar = ivars[i];
if ((object_getIvar(self, thisIvar) == property)) {
key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
break;
}
}
free(ivars);
return key;
}
As easy as
#define VariableName(arg) (#""#arg)
Then you do:
NSObject *obj;
NSString *str = VariableName(obj);
NSLog(#"STR %#", str);//obj
You can get the names of a class's instance variables with the Objective-C runtime API function class_copyIvarList. However, this is rather involved, rarely done and almost never the best way to accomplish something. If you have a more specific goal in mind than mere curiosity, it might be a good idea to ask about how to accomplish it in Objective-C.
Also, incidentally, person.name doesn't specify an instance variable in Objective-C — it's a property call. The instance variable would be person->name.
You might use preprocessor stringification and a bit of string twiddling:
NSUInteger lastIndexAfter(NSUInteger start, NSString *sub, NSString *str) {
NSRange found = [str rangeOfString:sub options:NSBackwardsSearch];
if(found.location != NSNotFound) {
NSUInteger newStart = NSMaxRange(found);
if(newStart > start)
return newStart;
}
return start;
}
NSString *lastMember(NSString *fullName) {
if(!fullName) return nil;
NSUInteger start = 0;
start = lastIndexAfter(start, #".", fullName);
start = lastIndexAfter(start, #"->", fullName);
return [fullName substringFromIndex: start];
}
#define NSStringify(v) (##v)
#define _NameofVariable_(v) lastMember(NSStringify(v))
If the person object is exposed as a property of the class, you can use objc_msgSend to get the value.
So, if you could access person using
[object person]
You could also do
objc_msgSend(object, "person")
For more details on message sending, including how to pass arguments to methods, see the Objective-C Runtime Programming Guide section on Messaging
The following works as a macro:
#define STRINGIZE(x) #x

Is possible send a array in Obj-c for a variable arguments function?

In python it is easy to build a dictionary or array and pass it unpacked to a function with variable parameters
I have this:
- (BOOL) executeUpdate:(NSString*)sql, ... {
And the manual way is this:
[db executeUpdate:#"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
#"hi'", // look! I put in a ', and I'm not escaping it!
[NSString stringWithFormat:#"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f]];
But I can't hardcode the parameters I'm calling, I want:
NSMutableArray *values = [NSMutableArray array];
for (NSString *fieldName in props) {
..
..
[values addObject : value]
}
[db executeUpdate:#"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,??values];
Chuck is right, there's no proper argument unpacking in Objective-C. However, for methods that require nil termination (NS_REQUIRES_NIL_TERMINATION), you can expand the variable list larger than what is needed by using an array accessor that returns nil when index >= count. This is most certainly a hack, but it works.
// Return nil when __INDEX__ is beyond the bounds of the array
#define NSArrayObjectMaybeNil(__ARRAY__, __INDEX__) ((__INDEX__ >= [__ARRAY__ count]) ? nil : [__ARRAY__ objectAtIndex:__INDEX__])
// Manually expand an array into an argument list
#define NSArrayToVariableArgumentsList(__ARRAYNAME__)\
NSArrayObjectMaybeNil(__ARRAYNAME__, 0),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 1),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 2),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 3),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 4),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 5),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 6),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 7),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 8),\
NSArrayObjectMaybeNil(__ARRAYNAME__, 9),\
nil
Now you can use NSArrayToVariableArgumentsList wherever you expect a nil-terminated variable argument list (as long as your array is smaller than 10 elements). For example:
NSArray *otherButtonTitles = #[#"button1", #"button2", #"button3"];
UIActionSheet *actionSheet = [[self alloc] initWithTitle:#"Title"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:NSArrayToVariableArgumentsList(otherButtonTitles)];
Unfortunately, no. Objective-C doesn't have argument unpacking like you get in a lot of modern languages. There isn't even a good way to work around it that I've ever found.
Part of the problem is that Objective-C is essentially just C. It does multiple argument passing with C varargs, and there's no simple way to do this with varargs. A relevant SO discussion.
I wanted to do the same thing. I came up with the following, which works fine, given some constraints on the input variables.
NSArray* VarArgs(va_list ap)
{
id obj;
NSMutableArray* array = [NSMutableArray array];
while ((obj = va_arg(ap, id))) {
[array addObject:obj];
}
return array;
}
#define VarArgs2(_last_) ({ \
va_list ap; \
va_start(ap, _last_); \
NSArray* __args = VarArgs(ap); \
va_end(ap); \
if (([__args count] == 1) && ([[__args objectAtIndex:0] isKindOfClass:[NSArray class]])) { \
__args = [__args objectAtIndex:0]; \
} \
__args; })
Using the above, I can call the following with either an NSArray or with varargs.
// '...' must be objc objects with nil sentinel OR an NSArray with nil sentinel
- (void)someMethod:(NSString *)sql, ...
{
NSArray *args = VarArgs2(sql);
// Do stuff with args
}
One more tip is to use the following in the prototype to have the compiler check for the nil sentinel to avoid potential bad things. I got this out of the apple headers...
- (void)someMethod:(NSString *)sql, ... NS_REQUIRES_NIL_TERMINATION;
There is a nice example how you can go from NSArray to va_list here (see "va_list in Cocoa" and "Creating a fake va_list" sections towards the bottom):
http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
Here is a teaser ("arguments" is NSArray):
char *argList = (char *)malloc(sizeof(NSString *) * [arguments count]);
[arguments getObjects:(id *)argList];
contents = [[NSString alloc] initWithFormat:formatString arguments:argList];
free(argList);
Not quite python or ruby, but hey...
You should use new FMDB version http://github.com/ccgus/fmdb. It has the method you need:
- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
To accomplish what you want, you have to use "varargs", as your method uses, or you can pass in an array of values, something like [db executeUpdate:sql withValues:vals];, and then pull out the values in the method. But there's no way to do something more "Pythonic", such as automatically unpacking a tuple of values, á la def executeUpdate(sql, *args).
Unfortunately (Objective-)C doesn't provide a way to do that. The executeUpdate method would need to accept an NSArray instead of variable argument list in this case.
However, if you do know the amount of entries in the array (you have the amount in the string in the example anyway), you can of course do something like
[db executeUpdate:#"insert into test (a, b) values (?, ?)", [values objectAtIndex:0], [values objectAtIndex:1]]
If executeUpdate is an external library method and that library does not offer a version of the method accepting an NSArray, you could come up with your own wrapper function. The function would take the query string and an array as argument. This function would then call the executeUpdate method with correct amount of arguments based on the length of the array, something along the lines of
if ([values count] == 1) {
[db executeUpdate:query, [values objectAtIndex:0]];
}
else if ([values count] == 2) {
[db executeUpdate:query, [values objectAtIndex:0], [values objectAtIndex:1]];
}
you could then call this new function as
executeUpdateWrapper(#"insert into test (a, b) values (?, ?)", values);
The obvious drawback in this solution is that you need to handle all possible lengths of the array separately in the function and it has a lot of copy-paste code.
additionally to robottobor's solution:
if you add the following macro:
#define splitAlternatingArray(args,arg1,arg2) \
NSMutableArray *arg1 = [NSMutableArray array];\
NSMutableArray *arg2 = [NSMutableArray array];\
{\
BOOL isFirst = YES;\
for (id arg in args) {\
if (isFirst) {\
[arg1 addObject:arg];\
} else {\
[arg2 addObject:arg];\
}\
isFirst = !isFirst;\
}\
}
you can then do tricky things like:
- (id)initWithObjectsAndKeys:(id)firstObject, ...{
NSArray *objKeyArray = VarArgs2(firstObject);
splitAlternatingArray(objKeyArray,objs,keys);
return [self initWithObjects:objs forKeys:keys];
}