how to pass enum as parameter to function in objectivec - objective-c

The header file looks like:
enum RatingsEnum
{
userRating,
criticRating,
};
#interface SFMovie : NSObject
- (NSNumber *)getRating:(NSDictionary *)movieDic :(enum RatingsEnum) rating;
#end
How can I use this method getRating? I am not sure how to pass the enum. My calling code:
- (void) testGetCriticRatingMethod{
NSMutableDictionary *ratingDictionary = [[NSMutableDictionary alloc]init];
[ratingDictionary setObject:#"Certified Fresh" forKey:#"critics_rating"];
[ratingDictionary setObject:#"70" forKey:#"critics_score"];
[ratingDictionary setObject:#"Certified Fresh" forKey:#"audience_rating"];
[ratingDictionary setObject:#"87" forKey:#"audience_score"];
SFMovie *movie = [[SFMovie alloc]init];
enum RatingsEnum ratings;
NSInteger userRating = [movie getRating:ratingDictionary rating:userRating];
}
This produces the following warning:
No visible #interface for 'SFMovie' declares the selector 'getRating:rating:'
Can somebody guide me to a good enum tutorial? Thank you all.

Change
- (NSNumber *)getRating:(NSDictionary *)movieDic :(enum RatingsEnum) rating;
to
- (NSNumber *)getRatingWithDictionary:(NSDictionary *)movieDic ratingEnum:(enum RatingsEnum) ratingEnum;
Change
enum RatingsEnum ratings;
NSInteger userRating = [movie getRating:ratingDictionary rating:userRating];
to
enum RatingsEnum ratings = userRating;
NSNumber *ratingFromUser = [movie getRatingWithDictionary:ratingDictionary ratingEnum:ratings];

This has nothing to do with the enum, or the type of the parameter at all. Your syntax is simply wrong. The name of the method as declared is getRating::. A correct call looks like
[movie getRating:ratingDictionary :userRating];
Add a label to the second parameter
- (NSNumber *)getRating:(NSDictionary *)movieDic ofType:(enum RatingsEnum)rating;

Related

Convert a CFNumberRef to NSInteger

#import "someClass.h"
#implementation someClass
- (NSInteger *)checkWakeOnLan {
SCDynamicStoreRef ds = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("myapp"), NULL, NULL);
CFDictionaryRef dr = SCDynamicStoreCopyValue(ds, CFSTR("State:/IOKit/PowerManagement/CurrentSettings"));
CFNumberRef wol=CFDictionaryGetValue(dr, CFSTR("Wake On LAN"));
CFRelease(dr);
CFRelease(ds);
here my problem, how to convert CFNumberRef to NSInteger,
i tryed again and again, but got anytime
"makes integer from pointer without cast"
NSInteger *value = [... ?];
return value;
}
- (IBAction)doStuff:(NSButton *)sender {
[myBevelButton setState:[self checkWakeOnLan]]; //setState takes NSInteger
//myBevelButton defined elsewhere, shows different icons
}
#end
Use this:
//assuming you want to convert wol to NSNumber and then to NSInteger
NSNumber *nsnumber = (NSNumber*)wol;
NSInteger value = [nsnumber integerValue];
*NOTE: You rarely need an NSInteger pointer. In your case you can quite easily use NSInteger. Also you need to change the method as - (NSInteger)checkWakeOnLan

Why isn't NSString answering to call by reference?

I'm trying to change a variable of NSString type by calling a method. But it doesn't work. I tried this very same method with othe types and it worked perfectly fine. Can you help me out there?
Interface:
- (void) changeNSString: (NSString *) stringToChange;
Implementation:
- (void) changeNSString: (NSString *) stringToChange{
stringToChange = #"Test";
}
- (IBAction)actionBla:(id)sender {
NSString *myString = [NSString string];
[self changeNSString:myString];
NSLog(myString); //Returns nothing or empty string
}
======================
Edit: thanks to the help of CrimsonDiego, here is a working solution. Though I still have a little understanding issue: why is there no type-error when assigning a NSMutableString to a NSString var?
Interface:
- (void) changeNSMutableString: (NSMutableString *) stringToChange;
Implementation:
- (void) changeNSMutableString: (NSMutableString *) stringToChange{
[stringToChange setString:#"Test"]
}
- (IBAction)actionBla:(id)sender {
NSMutableString *myString = [NSMutableString string];
[self changeNSMutableString:myString];
self.myLabel.text = myString; //Why is there no type error???
}
You are assigning a new variable to a local variable within the function.
The local variable stringToChange references the same variable as myString at first, but when you assign #"Test" to stringToChange, it no longer references the original string.
The solution here would be to use NSMutableString, or have a class variable (or property) and use that instead of passing it to the function.
Your setting stringToChange as local in your function. Try the following:
- (NSString*) changeNSString: (NSString *) stringToChange{
stringToChange = #"Test";
return stringToChange;
}
- (IBAction)actionBla:(id)sender {
NSString *myString = [NSString string];
myString = [self changeNSString:myString];
NSLog(myString); //Returns nothing or empty string
}
I am no expert on Objective-C but it seems to me that stringToChange, as in "normal" C, is a parameter that can contain a pointer to a string. The parameter is pass-by-value and thus, when you assign #"Test" to stringToChange you are actually only changing the local parameter stringToChange and not myString. If you want to change myString you have to change the parameter type to NSString ** and pass &myString as a parameter instead.

Method to instantiate objects

Would like to create a method that instantiate objects. 
- (NSArray *) make3Of : (Class) type
{
...
type * temp = [[type alloc] ...
...
}
But I get a warning from Xcode ...
Actual warning:
"Class method +alloc not found (return type defaults to 'id')"
Is there a better/correct way to do this?
Actual code:
- (NSArray *) getBoxesOfType: (Class <ConcreteBox>) type StartingFrom: (uint64_t) offset
{
NSMutableArray *valueArray = [[NSMutableArray alloc]initWithObjects: nil];
for (uint64_t i = offset; i< boxStartFileOffset + self.size; i += [self read_U32_AtBoxOffset:i])
{
if ([[self read_String_OfLen:4 AtBoxOffset:offset + 4] isEqual:[type typecode]]) {
[[type alloc]initWithFile:file withStartOffset:i]; //warning here;
//yes I plan to assign it to a variable
//(originally of "type" but that won't work as AliSoftware pointed out, will be using "id" instead.
...
}
}
}
Same as example, I'm trying to instantiate a couple of objects.
Code for protocol:
#import <Foundation/Foundation.h>
#protocol ConcreteBox
+ (NSString *) typecode;
- (id) initWithFile: (NSFileHandle *) aFile withStartOffset: (uint64_t) theOffset;
#end
You can't use a variable (in your case type)... as a type for another variable!
In your code, both type and temp are variables, that's a syntax error.
As you don't know the type of the variable as compile time, use the dynamic type id instead. This type is specifically designed to handle cases when the type is not defined at compile time.
So your code will look like this:
-(NSArray*)make3Of:(Class)type {
id obj1 = [[[type alloc] init] autorelease];
id obj2 = [[[type alloc] init] autorelease];
id obj3 = [[[type alloc] init] autorelease];
return [NSArray arrayWithObjects:obj1, obj2, obj3, nil];
}

Pointers in method params - objective-c

How to pass pointer as param in method?
for example:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray *) pointerToArray;
where simpleString is simple param, and pointerToArray is pointer to an array;
In Objective-C, strings and arrays are both classes. As you can see, they are already accessed through pointers. So you simply use them as the declaration says:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray *) pointerToArray;
And you invoke like:
NSString *s = #"Hello, world";
NSMutableArray *a = [NSMutableArray arrayWithObjects: #"Hello", #"silly", #"example", nil];
[yourClass dosomething:s :a];
FWIW, the name of your method is dosomething::. It is customary to denote each parameter, so I would call it:
-(void) doSomethingWithString:(NSString *)greeting array:(NSMutableArray *)strings;
then the name is doSomethingWithString:array: which is much more readable, IMO. You
invoke it with:
[yourClass doSomethingWithString:s array:a];
Like this:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray **) pointerToArray;
(Add a second '*' to the parameter type
In your method, you then do something like:
*pointerToArray = [NSMutableArray array];
For example:
NSString *localSimpleString;
NSMutableArray *localArray;
[self dosomething:localSimpleString :pointerToArray];

Get property name as a string

I need a way to pass a property and get the name assigned to it. Any suggestions?
#property (nonatomic, retain) MyObject *crazyObject;
NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return #"crazyObject"
You can try this:
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
NSLog(#"Names: %#", propertyNames);
It's as simple as this...expanding upon what Chuck already mentioned:
#ifndef STR_PROP
#define STR_PROP( prop ) NSStringFromSelector(#selector(prop))
#endif
You then use it like so:
NSString *strProp = STR_PROP(myProperty);
Background
Keep in mind that properties are really just, to quote Apple, "a syntactical shorthand for declaring a class’s accessor methods." In fact, by itself, the #property declaration doesn't even work. Your #synthesize statement translates the #property into the equivalent of two methods:
- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;
Which one is used depends on the context surrounding your self.crazyObject. (#synthesize also creates a matching instance variable if you didn't do it yourself.) The offshoot of all this is that you can't really translate to and from a property with one single method.
Proposed Solution
You can use what Apple already provides:
NSString *foo = NSStringFromSelector(#selector(myClassProperty));
Or do something custom:
Given that self.crazyObject really translates to either [self crazyObject] or [self setCrazyObject:foo] by the time your code is running, ou'll probably need two methods, like:
- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;
You might then want at least 2 companion methods such as:
- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;
Within these methods, you can use the Foundation functions NSStringFromSelector and NSSelectorFromString to convert back and forth between SEL and NSString. Use whatever string manipulations you like to convert back and forth between your setter string (setCrazyObject) and your property name (crazyObject).
A complete solution is hard to provide without knowing the exact use case, but hopefully this provides some more clues for anyone trying to accomplish something similar. There might even be some useful things made possible by combining this approach with Oscar's answer.
Here is a function that returns the name of an ivar, so basically it not only returns the properties but any ivar of the class. I haven't found a way to get the property directly so I used the ivar trick.
#import <objc/objc.h>
/// -----
- (NSString *)nameOfIvar:(id)ivarPtr
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
id pointer = object_getIvar(self, ivar);
if(pointer == ivarPtr)
{
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
break;
}
}
free(ivars);
}
return name;
}
After searching and debugging i find solution for me...
Added #import <objc/runtime.h>
Methods object_getIvar(id obj, Ivar ivar) send bad access and app crashes. i modify some code and it worked great:
+(NSString*)stringWithProperty:(id)property withClass:(id)controller
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([controller class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
if ([controller valueForKey:name] == property)
{
break;
}
}
free(ivars);
}
return name;
}
Modifying the solution, it works when your object is allocated already, otherwise it returns nil:-
NSString * NSStringFromProperty(NSObject* property, NSObject* class)
{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([class class], &propertyCount);
NSString *name = nil;
for (unsigned int i = 0; i < propertyCount; ++i)
{
name = [NSString stringWithUTF8String:property_getName(properties[i])];
NSObject *object = [class valueForKey:name];
if (object != nil && object == property)
{
break;
}
else
{
name = nil;
}
}
free(properties);
return name;
}
You can use
NSString *str = NSStringFromSelector(#selector(crazyObject));
The good thing about this approach is that:
Xcode will autocomplete word crazyObject for you.
When later on you will change the property name from crazyObject to myCrazyObject, Xcode will add a warning saying "unrecognized selector!" -- pretty good for debugging.
I use this method so often, that I even created a function, which allows to write less letters:
NSString * __nonnull sfs(SEL __nonnull theSelector)
{
if (!theSelector)
{
abort();
}
return NSStringFromSelector(theSelector);
}
Now your final solution can look like this:
NSString *str = sfs(#selector(crazyObject));
From Get property name as string, without using the runtime reference library, just define:
#define propertyKeyPath(property) (#""#property)
#define propertyKeyPathLastComponent(property) [[(#""#property) componentsSeparatedByString:#"."] lastObject]
And then you can do something like this:
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
You may check my approach at Gist to get the string for a property with autocompletion and compile-time check.
How to use:
Get the property name for a class:
#interface AnyClass : NSObject
#property (strong) NSData *data;
#end
// == My approach ==
// C string for a class
PropertyNameForClass(AnyClass, data); // ==> "data"
// NSString for a class
PropertyStringForClass(AnyClass, data); // ==> #"data"
// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = #"data";
Get the property name for a protocol:
#protocol AnyProtocol
#property (strong) NSDate *date;
#end
// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date); // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date); // ==> #"date"
Unconventional, hacky, ugly, late, but... as strong-named as it gets and works like a charm:
#define SOME_WAY_TO_GET_PROPERTY_NAME(p) p == p ? [[[[[[[NSString alloc] initWithCString:#p encoding:NSUTF8StringEncoding] componentsSeparatedByString:#"."] lastObject] componentsSeparatedByString:#" "] lastObject] stringByReplacingOccurrencesOfString:#"]" withString:#""] : #""
Sample usage:
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME(self.customer.surname)); // surname
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME([[self customer] birthDate])); // birthDate
...