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)
Related
I want to get the class name of an object as what we are using.
That means now if I write this code
NSString *s = [NSString string];
NSLog(#"%#",[s class]);
The output is __NSCFConstantString
How can I get it as NSString itself ?
Note : NSString is just an example
I know __NSCFConstantString is correct. But my intention is to get like NSString. Is there any way to acheive this?
Give these a try, they'll output NSString. Keep in mind, the second set requires importing the Objective-C runtime header.
#import <objc/runtime.h>
NSString *string = #"I'm a string.";
NSLog(#"%#",NSStringFromClass([string classForCoder]));
NSLog(#"%#",NSStringFromClass([string classForKeyedArchiver]));
NSLog(#"%s",class_getName([string classForCoder]));
NSLog(#"%s",class_getName([string classForKeyedArchiver]));
Now, this won't work in all cases. For example, trying to get the class of NSConstantString, in this manner will output NSString. If you require checking the class name as a string in this way, you probably should reconsider your approach to solving the problem.
NSString is a so-called "class cluster". That means that the init methods will return
an instance of some subclass (such as __NSCFConstantString or __NSCFString).
You will never get an instance with the class equal to NSString.
If your intention is to check whether an object is a NSString or not then
use isKindOfClass:
if ([s isKindOfClass:[NSString class]]) {
// this is a string …
}
Other examples of class clusters are NSNumber, NSDictionary, NSArray
and their mutable variants.
NSLog(#"%#", NSStringFromClass([s class]));
NSMutableArray *array = [[NSMutableArray alloc] init];
NSString *string = #"string";
[array addObject:string];
NSDate *date = [[NSDate alloc] init];
[array addObject:date];
for (*placeholder* stuff in array)
NSLog(#"one");
If I change placeholder to either NSString* or NSDate*, I expect to see "one", because the for loop should just ignore a non-matching type. However, the result is "one one".
Doesn't this imply that you should just have placeholder be id whatever the situation, since it doesn't seem to matter anyhow?
fast enumeration always iterates over all object in a collection. it does not filter.
The only thing that happens is, that you will have some strange casts.
if your array contains objects of differnt classes, you can determine the class for each object with isMemberOfClass:
if you would do for (NSDate *obj in array), any object in the array will be casts to NSDate, no matter if that is sense-full or not. and due to the nature of objective-c it will even work, as-long as you dont send a message that is only understandable by NSDate objects or send the object as an argument to a method that needs to receive a date object, as a cast does not change the object in anyway. A cast is just a promise you make to the compiler that you know what you are doing. Actually you also can call it a lie.
To answer your question title itself: You dont have to set the class inside the loop statement. the generic object type id is sufficient. But usually you have objects of one kind in an array — views, numbers, string, dates,…. by declaring the right class you gain some comfort like better autocompletion.
Yes, using id (or some other common ancestor class) is the correct approach, and then it's necessary to determine which type of class has been enumerated in order to handle it differently:
for (id obj in array)
{
if ([obj isMemberOfClass:[NSString class]])
{
NSString *str = (NSString *)obj;
NSLog("obj is a string: %#", str);
}
else if ([obj isMemberOfClass:[NSDate class]])
{
NSDate *date = (NSDate *)obj;
NSLog("obj is a date: %#", date);
}
}
The problem has nothing to do with fast enumeration, but with collections which can contain any type of object. The same question arises when you access an individual element of an array:
id lastObject = [array lastObject];
or
NSString *string = [array lastObject];
Which will you chose? It all depends on your code. If you're sure that array only contains strings, then in my opinion it is better to use the second choice, because you get additional type checking, autocompletion, and method matching from the compiler (i.e. you won't get warnings if you call a method that has different signatures for two different objects). The same applies to fast enumeration: if your collection can contain any kind of object, use id. If you know what it contains, use the specific type. (And the same also applies to block tests. In NSArray's method
- (NSUInteger)indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
if you know it only contains strings for instance, you can replace id with NSString * in the block arguments. It won't change at all the compiled code or the behavior of your application, it will only change the compiler type checking.
I got this code and XCode warns me of "incompatible pointer types initializing NSString *__strong with an expression of type UITextField".
NSString *name = (UITextField *)searchText.text;
but this one is fine
NSString *name2 = [(UITextField *)searchText text];
and this one is fine too
NSString *name3 = [(UITextField *)searchText.text mutableCopy];
I have two questions:
I am confused with obj.* and [obj *]
Why is the "mutableCopy" correct is this case?
I don't know how to search in Apple developer documentation for these questions.
In the first version, due to operator precedence, you're casting searchText.text to a UITextField*, what you want to do is probably to cast searchText;
NSString *name = ((UITextField *)searchText).text;
In the second version you don't have the dot, so the compiler understands your cast to be casting searchText to UITextField and send the text message to it. In other words, exactly right.
The last case is a bit tricky since it involves both runtime and compile time. As I understand it;
You cast searchText.text to a UITextField*. The runtime still knows that the object is an NSString and the mutableCopy message that exists on both will go to the correct method [NSString mutableCopy] anyway and create/return a mutable copy of the NSString so runtime it works out ok.
Since mutableCopy returns id (referencing a NSMutableString), the assignment to an NSString is ok by the compiler (id can be assigned to anything), so compile time it's ok.
A question, what is searchText originally? That the last version compiled without warning indicates that it's already an UITextField*, or at least a type that can take the text message. If so, you should be able to just do;
NSString *name3 = [searchText.text mutableCopy];
In the second and third examples, the cast just operates on searchText. So with these you are sending a message to a UITextField object.
In the first one, the cast applies to the whole of searchText.text. Assigning a UITextField object to a NSString variables is not what you want. The code you're looking for is:
NSString *name = ((UITextField *)searchText).text;
The mutableCopy message returns a copy of your string as a NSMutableString object, which can be assigned to a NSString as NSMutableString derives from it. In this case using the 'copy' message is just as good.
Hope that helps.
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];
Consider the following code:
if([obj isKindOfClass:[NSString class]]) {
NSString *s = [(NSString *)obj stringByAppendingString:#"xyzzy"];
}
I'm a bit confused here. The if statement checks whether or not obj is of the NSString class. If it is, it assigns the object and an appended string to NSString *s, do I understand this correctly? If so, why would you still cast it to (NSString *)?
Doesn't the if statement already check for that and doesn't that make the typecasting unnecessary?
Wouldn't it be perfectly fine to just say:
NSString *s = obj stringByAppendingString:#"xyzzy"];
Thanks in advance.
It all depends on how obj is defined. If it is id obj then no casting is needed, but if it was defined as NSObject *obj the cast is necessary to suppress the compiler warning that stringByAppendingString: is not defined on NSObject. The cast is not needed to make the code work at runtime, it only tells the compiler the "correct" type so it can tell whether the method should exist on the object.
The reason why the cast isn't needed for id is because id means "an object of any type", while NSObject * means "an object of type NSObject".