I've a NSDictionary that contain non omogeneus stuff. I need to understand what types of memebers there are into.
With the [dict description] I understand the values into, but not the type.
There is a way to do that?
Thanks
You can ask each object for it's class (type) and then check if it's the type you want, using NSObject's -isKindOfClass: method. Note that if your object is, say, an NSMutableArray this would return true for [myMutableArray isKindOfClass:[NSArray class]]
Example:
id object = [myDict objectForKey:aKey];
if ([object isKindOfClass:[NSString class]])
// It's an NSString!
Source: developer.apple.com
Related
Shortly: how to determine if id is CFType or not at runtime
i'm implementing dynamic core data attributes and in willSave method of ExtendedManagedObject i wanna check if the id value is CFType to store it into plist file.
If I'm trying to save to plist UIImage, that is not toll-free bridged with CF (apple docs), I am getting an error:
2011-11-17 17:16:25.294 [490:707] Error saving extended data: Property list invalid for format (property lists cannot contain objects of type 'CFType')
Can I check it with some method or I have to implement by myself (just isKindOfClass from docs)?
I don't want to implement accessors in NSManagedObject subclass, I dont know exactly how many urls I'll get from entities properties. Question is about dynamic extended attributes at runtime.
- (void)willSave
{
NSDictionary *changes = [self valueForKey:#"extendedChanges"];
if (changes!=nil) {
// merge changes into snapshot
NSMutableDictionary *dict = [[self extendedSnapshot] mutableCopy];
NSEnumerator *e = [changes keyEnumerator];
NSString *key;
while (key=[e nextObject]) {
id value = [changes objectForKey:key];
if (value==[NSNull null])
[dict removeObjectForKey:key];
else if (#ugly and I'm not shure is thread safe **else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSDate class]] || [value isKindOfClass:[NSData class]] || [value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]])**)
[dict setObject:value forKey:key];
}
// archive as binary plist
NSData *data = nil;
if ([dict count]>0) {
NSString *error=nil;
data = [NSPropertyListSerialization dataFromPropertyList:dict
format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
if (error!=nil) {
NSLog(#"Error saving extended data: %#", error);
[error release];
}
}
[dict release];
[self setPrimitiveValue:data forKey:#"extendedData"];
}
[super willSave];
}
That's a wrong way to approach the problem. The blog post you referenced saves the extended attributes as a serialized plist. The plist can contain only the following types of objects, as written in the official doc:
NSArray, NSDictionary, NSDate, NSData, NSString and NSNumber.
Other classes are just not allowed. Don't add objects of any other class to the attributes extended this way.
Also, saving an image file in a CoreData database is not a good idea, generally speaking, read On Blobs in the official doc. Instead, save the UIImage in a file and write the file path in the CoreData database.
If you just have a transient property which is not saved to the database, you don't even have to go through the trouble of creating extended attributes. Just add it as a property of a subclass of NSManagedObject:
#interface MyManagedObject: NSManagedObject {
}
#property(retain, nonatomic) UIImage* thumbnail;
#property(retain, nonatomic) NSDictionary* thumbnailDictionary;
#end
without adding thumbnail in the CoreData model. Then do the corresponding #synthesize in the .m file. The property added this way to a managed object is just not saved.
If you want to keep unknown number of thumbnails, you can put an NSDictionary (or NSArray) containing UIImage*s.
If you really do want to get CFType from an NSObject*, do the following:
NSObject* x= ...;
CFTypeID typeID=CFGetTypeID((CFTypeRef)x);
if(typeID != CFStringGetTypeID() &&
typeID != CFDateGetTypeID() &&
typeID != CFDataGetTypeID() &&
typeID != CFDictionaryGetTypeID() &&
typeID != CFArrayGetTypeID() ) {
... it's a non-plistable type ...
}
The ugly if clause is there because there's no public function which gives you the CFTypeID of an un-bridged Objective-C object.
Read CFType reference. But I don't recommend doing this.
Trying to store an image in UIImage format also results in "property lists cannot contain objects of type CFType error. Best way is to convert UIImage to NSData before writing to pList.
As per subject, how can I check whether an object is an NSArray or NSDictionary?
if([obj isKindOfClass:[NSArray class]]){
//Is array
}else if([obj isKindOfClass:[NSDictionary class]]){
//is dictionary
}else{
//is something else
}
Try
[myObject isKindOfClass:[NSArray class]]
and
[myObject isKindOfClass:[NSDictionary class]]
Both of these should return BOOL values. This is basic use of the NSObject method:
-(BOOL)isKindOfClass:(Class)aClass
For a bit more information, see this answer here:
In Objective-C, how do I test the object type?
Consider the case when you're parsing data from a JSON or XML response. Depending on the parsing library you are using, you may not end up with NSArrays or NSDictionaries. Instead you may have __NSCFArray or __NSCFDictionary.
In that case, the best way to check whether you have an array or a dictionary is to check whether it responds to a selector that only an array or dictionary would respond to:
if([unknownObject respondsToSelector:#selector(lastObject)]){
// You can treat unknownObject as an NSArray
}else if([unknownObject respondsToSelector:#selector(allKeys)]){
// You can treat unknown Object as an NSDictionary
}
Just in case anyone comes late to this party looking for a Swift equivalent, here you go. It's a lot more elegant than the Objective-C version, IMHO, because not only does it check the types, but it casts them to the desired type at the same time:
if let arrayVersion = obj as? NSArray {
// arrayVersion is guaranteed to be a non-`nil` NSArray
} else if let dictionaryVersion = obj as? NSDictionary {
// dictionaryVersion is guaranteed to be a non-`nil` NSDictionary
} else {
// it's neither
}
In objective C, what is a better way of doing this:
if ([response class] == [nil class])
response is either a NSDictionary or NSMutableDictionary
The code is working but I'm getting a "Invalid receiver type 'void *' or "comparison of distinct Objective-C types 'Class' and 'struct NSMutableDictionary *' lacks a cast" warning messages
Thanks
If you're actually looking to test [response class], instead of the value of response itself, you'll want to use
if ([response isKindOfClass:[NSNull class])
If you're looking to check if response itself is nil, I describe a nice way of doing this in this answer to a similar question.
An object's class can't be nil unless you've botched some runtime swizzling. All valid objects must be instances of a valid class. Are you trying to do something like the following?
if (!response) {
// 'response' is nil
} else if ([response isKindOfClass:[NSMutableDictionary class]]) {
// response is an NSMutableDictionary
} else if ([response isKindOfClass:[NSDictionary class]]) {
// response is an NSDictionary
// (or an NSMutableDictionary if you remove the above 'if')
}
Since you mentioned either NSDictionary or NSMutableDictionary and appear to be testing for an instances kind...
isKindOfClass: will identify whether or not the receiver is an instance of the class specified. This includes subclasses.
Note that you cannot use this to determine if a dictionary is mutable or immutable, though, as dictionaries are instances of NSCFDictionary which is a subclass of `NSMutableDictionary.
This is very much on purpose.
I have a NSDictionary with a NSString and NSArray
I have to save only the NSArray in a variable without knowing the key.
Is this possible?
If I'm understanding you correctly, you have a dictionary that contains both an NSString and an NSArray, and you want to extract just the NSArray, without knowing what the key is.
One way to do that is to look through the dictionary with fast enumeration:
NSString *key;
for(key in someDictionary){
id someObject = [someDictionary objectForKey: key];
}
and then look at the objects to see which one is an NSArray:
if ([someObject isKindOfClass:[NSArray class]]) {
// do something with the array
}
(obligatory warning: explicitly checking an object's class is often a sign of a flawed design. In most cases, you should be checking for behavior (-respondsToSelector), not class identity)
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name, related stuff and also check if the respective object is NSString or not. How should I go about it?
I was trying something like the following. It wasn't working of course.
for(NSString *string in array){
NSLog(#"Name of the class : %#", [NSString stringWithCString:class_getName(Class id)];
If you're on Mac OS X, you can use [object className], it returns an NSString
for(id obj in array) NSLog(#"Name of the class: %#", [obj className]);
To check if it's a NSString, you should use something like this:
for(id obj in array) {
if ([obj isKindofClass:[NSString class]]) {
// do something
}
}
for(id object in array){
NSLog(#"Name of the class: %#", [object className]);
NSLog(#"Object is a string: %d", [object isKindOfClass:[NSString class]]);
}
Take a look at the NSObject class and protocol for other interesting methods.
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name & related stuff & also check if the respective object is NSString or not.
Hold up. Why do have an array of different typed objects in the first place? Could you redo your design to avoid getting into that situation?
As others have said, -isKindOfClass: works. One downside is it generally leads to brittle code. Here your loop needs to know about all the classes that could be in the array. Sometimes this is the best you can do though.
Designs that use -respondsToSelector: tend to be a little more robust. Here your loop would need to know about the behaviors it depends on of classes in the array.