The following code:
[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:mapURL]]
returns an instance of NSConcreteData as opposed to NSData (which I expected!). What does NSConcreteData represent, exactly, and why is it being returned instead of an instance of NSData? Further, where is this class defined? XCode is complaining that it hasn't been defined, but I don't know what I should include in order to get the class.
Any help appreciated!
The Foundation framework uses class clusters in certain areas to provide a common interface to various classes. What this means is that, when you use an NSData API to create an NSData object, the actual class that is instantiated and returned is different from NSData, but can and should be treated and referred to as an NSData object by your code.
At the end of the day, the simple answer is: ignore the existence of NSConcreteData and treat it like NSData.
For more information on class clusters, please see the Class Clusters section of Apple's Cocoa Fundamentals Guide.
Related
As we know it is very easy to make conversion between toll-free bridged types and objective-c types such as CFArrayRef and NSArray. Yesterday, I found a piece of code that convert CGImageRef (which is not a tool-free bridged type) to id as below:
CALayer *imgLayer = [CALayer layer];
imgLayer.contents = (id)aImage.CGImage;
However, it works fine. But I don't understand it, because CGImageRef is not belong to toll-free bridged types according to apple's document. Look at the second line code, imgLayer should retain the value assigned to the property contents. Because CGImageRef is converted to id so I think in the property contents' setter method it will send a retain message to that object like this [xxx retain]. But aImage.CGImage is not an object so I don't think this retain message will perform correctly (however it will). So my question is what is the actual action behind this conversion?
These methods are an exception to the rule that Apple added for convenience. Since the compiler knows about Objective-C naming conventions and the implied memory management, Core Foundation objects returned by objective-C functions will be automatically converted and handled correctly.
See the section The Compiler Handles CF Objects Returned From Cocoa Methods in this document
Toll-free bridging is a bit of an outdated term. It referred to the seamless casting between Core Foundation C objects and Objective-C objects. However, with ARC the transition is no longer as seamless since the compiler needs hints about what to do with the corresponding objects after bridging them. In this case, though, it gets its hints from the naming conventions of the method.
Furthermore, all Core Foundation objects can essentially be cast to Objective-C objects. If there is no corresponding type then they will simply become a special catch-all class (NSCFType) that was designed for this purpose.
I want to serialize an object to an NSDictionary.
(key = value) will be (property-name = property-value)
If a property is another object, it will be recursively serialized as another NSDictionary inside the parent NSDictionary.
Does anyone have an idea? API? Pointer? Reflection? framework?
Thanks!
For what purpose do you need to turn an object into a dictionary?
If it is to save the object to a file, you should look at the NSCoder protocol, which you can implement for your object so that an array of your objects (for example) can be serialized by built in methods like NSArray's -writeToFile:atomically:.
Adopting NSCoding is certainly an option -- basically, your graph's elements would all need to know how to serialize/deserialize themselves (by adopting NSCoding). Under this approach, you would use the NSCoder interfaces for archiving and unarchiving.
Another common alternative would be a property list. This is a simpler more portable format with some restrictions to types. Namely, NSString, NSArray, NSDictionary, NSNumber (and CFBoolean), NSData, and NSDate - as well as their CoreFoundation counterparts. These types and collections all know how to serialize their elements appropriately. Here, you can use NSPropertyListSerialization for archiving and unarchiving. This is not much overhead for smaller tasks, but NSCoding is generally a better long term solution where portability is not a concern (e.g. export as XML) because the objects can maintain their serialization implementations without much intervention from the archiver.
For custom objects, you would typically support either a property list representation and/or NSCoding - but the collections types already know how to archive and unarchive themselves (provided of course their contents also support the approach to serialization you use).
More details can be found in Apple's 'Archives and Serializations Programming Guide'.
So, I understand that Categories in Objective-C can be used to add methods to classes without the need for subclassing. I also understand that these Categories cannot be used to add instance variables to classes.
I did a little bit of reading about Class Extensions, which can be used to add instance variables, but I don't understand how I can use Class Extensions to modify an existing class such as NSData.
My problem is the following:
I have a Core Data Model that contains a NSURL and NSData. The NSData displays the data for the NSURL. When a view needs to display the data, I do the following check:
--- If [NSData bytes] > 0, display the NSData.
--- Otherwise, fetch the data at NSURL and display the data when it returns
Simple enough. However, I run into problems when the NSURL is updated. So, if I modify the NSURL path with a new image, because [NSData bytes] is already greater than 0, I don't make the additional call to fetch the new image.
What I would like to do is add an instance variable to NSData called URLKey that would hold information about where the data comes from. I can't subclass NSData because I'm using CoreData.
Does anyone know some simple solutions for this? Perhaps there's a gap in my understanding of Class Extensions, or maybe there's just no simple way.
Class Extensions should be used on classes you implement yourself as a way of keeping ivars and some properties hidden from the header File, that should contain only stuff that should be visible outside the class (and ivars are't that kind of stuff).
Categories are used on classes already implemented, as a way of adding additional functionality. They are usually needed when you want to add a general kind of behavior to a known Class. E.g. adding a method to NSString +(NSString*)reversedString; that returns a reversed instance so you can then use it like this:
NSString *someString = #"string";
NSString *reverse = [someString reversedString];
NSLog(#"%#", someString); //this would output "gnirts"
.
Regarding your particular problem, I can assure you that your CoreDataModel does not contain NSURL or NSData. The supported types are primitives, strings, binary Data and transformables. So, if you want to, you can subclass NSData or NSURL and then use it with CoreData by setting the type to "transformable". And after you have done this, you can then subclass NSData as you wish and use class extensions in the process, or just use a category to add the methods you require to the class.
Quote from Apple about transformable attributes:
The idea behind transformable attributes is that you access an
attribute as a non-standard type, but behind the scenes Core Data uses
an instance of NSValueTransformer to convert the attribute to and from
an instance of NSData. Core Data then stores the data instance to the
persistent store.
What is __NSArrayI and __NSArrayM?
__NSArrayI(or M) cause "unrecognized selector" error.
How to convert to NSArray?
I did test to parse json, twitter api.
http://api.twitter.com/1/followers/ids.json?cursor=-1&screen_name=twitterapi
==> works fine. parsed object is NSCFDictionary class.
(This dictionary contains __NSArrayM class)
http://api.twitter.com/1/statuses/user_timeline.json?&screen_name=twitterapi
==> error. parsed object is __NSArrayM class.
__NSArrayI is a code-word for an immutable array - that is, a "regular" NSArray which you cannot change.
__NSArrayM is a code-word for a mutable array - that is, NSMutableArray. In NSMutableArray, you can add and remove items.
These are classes of the private api. There is a project where you can see all classes of the private api. You are not allowed to use them inside an app for the app store but sometimes it is useful too see how to access the objects and also what kind of object it is. They cannot be converted. I think, getting these kind of objects inside the debugger is just the representation of internal classes, for the classes you are using inside your project. Knowing what kind of class it is, lets you also understand where to look for the problem inside your code.
Here you can see a short lookup of both:
__NSArrayI
__NSArrayM
It is private classes. You shouldn't want to access them or moreover convert them.
If I'm not mistaken NSArray is subclass of _NSArray.
If you are adding/removing some objects to/from your array check that it is of mutable type : NSMutableArray
I want to populate [MyClass class] from a JSON string.
I use json-framework to get the NSDictionary, and it's dead easy to instantiate and setValue: forKey:... on my data object.
But for more complex data objects with classes as members of MyClass,
ie:
MyOtherClass *classNoTwo
I tried with
Class test = object_getClass(myClass.classNoTwo);
id foo = [[test alloc] init];
But foo is nil/null. The only way I found to get around it is to in my init method of MyClass is to alloc memory for it, and later replace it.
I would also like to know how to get rid of the myClass.classNoTo reference, as I am not supposed to know all the properties in my general parser.
Please don't just tell me to read the documentation, because I've done that, and I can't figure it out.
Thanks in advance
Try calling class_getProperty() to access a property of a particular name and then property_getAttributes() on the property returned by the first function. This will return a string that encodes the property's attributes, including the type. The format of the string is explained in Property Type Strings in the documentation.
Finally, when you have derived the type of the property, use NSClassFromString() to instantiate an object.
Also see the docs for the two functions mentioned for more details.
I have written a very simple dependency injection container called Factory. I do not get your question entirely, but the problems we solve look similar. Take a look at my sources, they are very simple and should get you started. You might be interested especially in the ClassAnalyzer class. It’s just a hack, but you should be able to get what you want from there.