What's 'void' on NSArray? - objective-c

I defined a NSArray in a header file like this:
#property (nonatomic, retain) NSArray *ages;
In my implementation I want to set this variable like this:
ages = [self setAges:[ageValues allKeys]];
'ageValues' is a NSDictionary. So what I do is just setting the array of keys to my self-defíned array. Strange enough, I get the following error message:
Asssigning to 'NS Array *' from incompatible type 'void'
But where can I find something void here? In my opionion I am just setting another array ([ageValues allKeys) to my own array and I can't find anything void???

The setAges: method is a method that returns void, in other words: it returns nothing (not even nil or something; it literally is not returning anything). Now you cannot assign "nothing" to a variable.
That being said, your code wants to do the same thing twice. All you want to do is simply:
self.ages = [ageValues allKeys];
or:
[self setAges:[ageValues allKeys]];
They do exactly the same, but use different syntax (the compiler transforms the first into the second).

setAges is a void method since it's a setter. As such, it returns void and you're then trying to assign it to your ages member. All you need to do is call setAges.
[ self setAges:[ ageValues allKeys ] ];

self.ages = [ageValues allKeys];
or
ages = [[ageValues allKeys] retain];

[self setAges:[ageValues allKeys]]; returns void.
If you have synthesized your ages property in your .m like so:
#synthesize ages;
The setter is automatically generated for you, so all you need to do is
self.ages = [ageValues allKeys];

Related

I'm looking for cleaner way to use dot notation in Objective-C

I have a class as following, and I use NSMutableArray to store a list of MyClass objects.
#interface MyClass: NSObject{
NSString* _name;
}
#property(nonatomic)NSString* name;
NSMutableArray* array = [NSMutableArray arrayWithCapacity:2];
[array addObject:[[MyClass alloc]init]];
[array addObject:[[MyClass alloc]init]];
I have to do the cast to get the name from array.
((MyClass*)array[0]).name
Is there a cleaner way to reference name from MyClass ?
I don't know why #matt didn't show the syntax for lightweight generics in his answer, but here it is:
NSMutableArray<MyClass *>* array = [NSMutableArray arrayWithCapacity:2];
Now the type of array[0] is MyClass * instead of just id, so this works:
array[0].name
Yes. If array will consist only of MyClass objects, you can use lightweight generics to give the compiler a hint about what it contains, thus making the cast unnecessary.
Also, note that even without generics, you can assign without casting (because an id can be assigned into any object reference):
MyClass* c = array[0];
NSString* name = c.name;
Or, as #rmaddy points out, give up on dot-syntax altogether and call the getter directly; this works because any message can be sent to an id:
NSString* name = [array[0] name];
Alternatively, use KVC to get the name properties as an array:
NSArray* names = [array valueForKey:#"name"];
Objective-C still doesn't know what names consists of (unless you use lightweight generics), but now you can say e.g.
NSString* name = names[0];

Constant value of NSString representation

I have a PList where I load a couple of rows of data in a dictionary. I want to add the a line like
<key>StandardValue</key>
<string>STANDARDVALUEFORCERTAININSTANCE</string>
Now when I read out the values I get a NSString. How can I get the value of the constant that I previously defined with
#define STANDARDVALUEFORCERTAININSTANCE 123
Is there a way to get the constant representation of a string? So essentially to parse it?
What you want to do isn't exactly possible. The constants created with #define only exist at compile-time, and at run time there is no way to access them by name - they have been converted to the constant value already.
One alternative that might exist is to define a number of methods that return constant values, say in a Constants class. Then, at run time, load the name of the method from the plist and call it using NSSelectorFromString() and performSelector:.
However, a possible issue with this is that for safety with performSelector: you'd have to rewrite all your constants as Objective-C objects (since performSelector: returns type id). That could be quite inconvenient.
Nevertheless, here is an example implementation of the Constants class:
#implementation Constants : NSObject
+ (NSNumber *)someValueForACertainInstance
{
return #123;
}
#end
And example usage:
NSDictionary *infoDotPlist = [[NSBundle mainBundle] infoDictionary];
NSString *selectorName = infoDotPlist[#"StandardValue"];
SEL selector = NSSelectorFromString(selectorName);
NSNumber *result = [Constants performSelector:selector];
And how the selector name would be stored in the info plist:
<key>StandardValue</key>
<string>someValueForACertainInstance</string>
You can't do it this way. I suggest a nice alternative: KVC.
You declare this variable as class instance:
#property (nonatomic,assign) int standardValueForCertainInstance;
Then you get the value with valueForKey:
NSString* key= dict[#"StandardValue"];
int value= [[self valueForKey: key] intValue];

what is this weird code notation mean

what's this line mean when using the second NSDictionay beside the message body:
NSDictionary *item = (NSDictionary *) [self.content objectAtIndex:indexPath.row];
(NSDictionary *) a type cast. It tells the compiler to assume that the object returned by the objectAtIndex: method is of the type NSDictionary * even though the return type of the method is different.
self.content is a property of type NSArray (I guess!)
This line returns you the Object (which seams to be a NSDictionary) at Index indexPath.row. (NSDictionary*) casts the object to NSDictionary.
This is a cast, as in C.
In your case, "self.content" seems to be an NSArray. So [self.content objectAtIndex:indexPath.row] would be an NSObject. Except that here, for some reason, you know it's an NSDictionary. So you explicitly cast it in order to avoid a compiler warning (that would tell you "hey, you're assigning an NSObject to an NSDictionary variable)

Objective-C handling integer values

I am getting confused with how to handle Integers in Objective C.
If I define the following:
NSInteger i = 6;
NSLog(#"%d", i);
I expect it to print 6 to the console.
however I have an NSInteger within an object which is obviously reference by a pointer so I get very difference results.
For example:
#interface Section : NSObject {
NSInteger Id;
}
#property (nonatomic, assign) NSInteger Id;
Please assume this has been synthesized in the implementation.
I create the object set its value and access it again as follows:
Section *section = [[Section alloc] init];
section.Id = 6;
NSMutableArray *sections = [[NSMutableArray alloc] init];
[sections addobject:section];
Section *sectionB = [setions objectAtIndex:0];
NSLog(#"%d", sectionB.Id);
This has the strange effect of printing the memory address ie a number like 5447889. Why can I not just get the value?
I have tried using:
NSInteger sid = [[section Id]integerValue];
But I then get the warning Invalid receiver type 'NSInteger' and sometime get an error Program received signal: “EXC_BAD_ACCESS”.
I would really like to know how to handle Integers, or any values for that matter properly.
Many Thanks
It looks like you're accessing uninitialized memory; 5447889 doesn't look like a pointer value—pointers are usually word-aligned, and 5447889 isn't word aligned.
Maybe you could cut and paste your actual code. This has typos such as addobject instead of addObject and setions instead of sections.
Does it work if you keep things simple and do NSLog(#"%d", section.Id) to skip messing with the array?
Regarding the strange values, see Dominic Cooney's answer.
Regarding the EXC_BAD_ACCESS: [[section Id]integerValue]; doesn't work because it then tries to interpret the NSInteger as an object and tries to send the message integerValue to it, which can't work. It's an integral number, not an object.
Think I found the answer to my own question. Setting the value of an NSInteger to a value of say 6 is fine. The problem I have is I am setting it to the value returned from a Json string using the following:
NSInteger i = [jsonResult objectForKey:#"Id"];
which should be:
NSInteger i = [[jsonResult objectForKey:#"Id"] integerValue];
I have not tested this but makes sense based on what DarkDust said about integerValue taking an object and not an Integer.
Thanks for your input.

How to use #encode() to get #"NSArray" in Objective-C

I'm using the runtime functions to get the type of a property (thanks to eJames for helping me to figure out this way).
The attribute string of the property looks like this:
T#"NSArray",&,Vstuff
I need to check if the property type is an array, at the moment I'm doing it like this:
- (BOOL)valueForKeyIsArray:(NSString *)key fromTagret:(id)target
{
NSString *lowerCaseKey = [self convertToKVCKey:key];
objc_property_t property = class_getProperty([target class], [lowerCaseKey UTF8String]);
NSString *propertyAttrs = [NSString stringWithUTF8String:property_getAttributes(property)];
NSString *encodedType = #"#\"NSArray\"";
NSRange range = [propertyAttrs rangeOfString:encodedType options:NSLiteralSearch];
return range.location != NSNotFound;
}
But since Apple can change the type definition string at any time, I would like to generate this #"NSArray" type string. I tried it with #encode(), but it did not work:
NSString *encodedType = [NSString stringWithUTF8String:#encode(NSArray *)];
So how can I generate this type string? Or is there a better way to check if this property attributes contain the array type?
There is no way to check this. In Objective-C source code the variables being typed as NSArray * is only there for the compiler to issue warnings. It has no meaning, and does not exist at runtime. If you mis-typed an NSArray as an NSString, you would get lots of warnings when compiling, but your code would behave exactly the same when run. At runtime all that is known is that the ivar/property is "an object".
Another way to think of it, is that once Objective-C is compiled, all object references are id references.
Just accept that if the runtime changes, your code will break, and move on. However, I think you might be miscategorizing ivars of type NSMutableArray *, CFArrayRef, or CFMutableArrayRef. You also seem to be assuming all keys correspond directly to a declared property.
The cleanest solution might be to assert that the sample object being used for the test (the target) must have a non-nil value for that key, and just grab the value and test that [[target valueForKey:key] isKindOfClass:[NSArray class]].