What is the content of character attributes like NSFontAttributeName? - ios

I was reading the apple documentation to learn how to format text for iOS. I came across an attributed string and most of it made sense to me. However, while looking up the different kinds of attributes, I saw that they are all declared as NSString.
For example:
NSString *const NSFontAttributeName;
NSString *const NSParagraphStyleAttributeName;
...
...
We pass in these string objects in a dictionary with the value being that particular attribute (e.g., a UIFont object). However, what I do not understand is what the content of that string has to do with the attribute itself. Do they just contain the name of the attribute? (i.e NSFontAttributeName might contain a string like #"NSFontAttribute")
Surely there must be a reason why has apple to chosen to do it this way?
Edit: My question isn't about why they use a string object as a key to the dictionary but why they use a predefined constant string object named NSFontAttributeName instead of allowing us to manually pass in a string #"NSFontAttribute" as the key. That's why I wondered whether the contents of their predefined string object has anything to do with this.

In this case, Apple uses an NSDictionary to contain an arbitrary set of key/value pairs. Doing it this way means you have a lot of flexibility because you can have no attributes, one attribute, or two dozen attributes with the same programming interface. And if in two years time there are attributes available that you haven't even thought about, Apple doesn't have to introduce any new APIs to support you setting these attributes.
The reason for using a constant instead of a string literal is that the compiler can save you if you misspell a name. If you wrote #"NSFontattribute" instead of #"NSFontAttribute", the compiler wouldn't know that you got it wrong.

As the attributes are expressed in an NSDictionary and you can only hold Objective-C objects in Objective-C collection classes, you have to use an object of some sort. They could have used enumerated integer values, wrapped in NSNumber objects, instead:
typedef enum {
KEYONE,
KEYTWO
} KeyValues;
NSDictionary *attributes = [NSDictionary dictionary];
attributes[#(KEYONE)] = #"The attribute";
One advantage of using an NSString as a key is that it's easier to debug at the slight expense of generating the dictionary key hash from the string (which must be very slightly more expensive than generating it from an NSNumber object).

Related

NSMutableDictionary contents inconsistent with output of allValues

So long story short, there's a discrepancy between the output of a NSMutableDictionary's contents and the result of calling allValues on the same object. Below is some debugger output after inspecting the object which demonstrates my problem: (made generic of course)
(lldb) po self.someDict.allKeys
<__NSArrayI 0xa5a2e00>(
<SomeObject: 0xa5a2dc0>,
<SomeObject: 0xa5a2de0>
)
(lldb) po self.someDict.allValues
<__NSArrayI 0xa895ca0>(
0.5,
0.5
)
(lldb) po self.someDict
{
"<SomeObject: 0xa5a2dc0>" = (null);
"<SomeObject: 0xa5a2de0>" = (null);
}
So as we can see, the actual output of the NSMutableDictionary contains null values for both its entries, but the contents of .allValues contains the proper data. These three outputs were taken at the same time in execution.
I'm not sure why this is happening, but I think it may have something to do with the fact that I'm encoding/decoding the object which this dictionary is a property of using CoreData. I believe I'm doing this properly:
[aCoder encodeObject:self.someDict forKey:#"someDict"];
and to decode
self.someDict = [aDecoder decodeObjectForKey:#"someDict"];
The weird thing is that if I inspect the dictionary before it ever gets encoded, it is still in the state described at the beginning of the post, so this is why I'm doubtful the CoreData actions are screwing with the contents.
As always, please don't hesitate to request additional information.
EDIT: The problem was as answered below. I was using a custom class which didn't cooperate with isEqual, so my solution was to change the storage and structure of my logic, which made using a Dictionary unnecessary.
I have not been able to duplicate the problem using NSString as keys and NSNumber as values. I suspect that your custom class does not properly implement hash and/or isEqual. Specifically, the results from those two methods must be consistent, meaning that if isEqual returns true, then the hash values for the two objects must be identical.
You also need to ensure that your class implements NSCopying properly and that a copy is equal to the original.
As a general rule, don't use custom objects for dictionary keys. Just use strings and be done with it.
As user3386109 points out, custom objects must properly implement the -hash and -isEqual methods in order to be used as dictionary keys, and even then, custom objects don't work correctly for dictionary keys for things like key/value coding.

Does NSNumber automatically cast strings?

When creating an SKStoreProductViewController, I pass a dictionary with a parameter for the store identifier. :
#{ SKStoreProductParameterITunesItemIdentifier : #010101010 };
This value is supposed to be an NSNumber (as it is above):
The value associated with this key is an instance of NSNumber, representing the iTunes identifier for the item you want the store to display when the view controller is presented.
But it works without complaint when I pass the value as a string:
#{ SKStoreProductParameterITunesItemIdentifier : #"010101010" };
What's going on here? Is NSNumber automatically creating the correct number type from the string that it's given? Is this occurring in the NSNumber or is StoreKit doing this?
Actually, thinking about it...
Initially I thought they must be converting the NSString into an NSNumber before doing whatever they need to do to get the information you are looking for.
However, on second thought...
I would guess that StoreKit is using the value against SKStoreProductParameterITunesItemIdentifier in a string. In which case they would do something like...
NSString *someStringToGetTheResults = [NSString stringWithFormat:#"thisIsThePath...?storeKitID=%#", dictionary[SKStoreProductParameterITunesItemIdentifier]];
This will be the same whether you pass in #12345 or #"12345".
Possibly...
No real way to tell though.
The docs say that the value stored in the SKStoreProductParameterITunesItemIdentifier key is supposed to be an NSNumber. Saving anything else in that key may work today, but may also stop working after any OS release, so don't do it.
As others have suggested, it's pretty likely that the store kit is fetching the value of the SKStoreProductParameterITunesItemIdentifier key, assuming it's an NSNumber, and sending it an integerValue method to get it's numeric value. You got lucky since NSNumber and NSString both have an integerValue method.

Multiple KeyMappers per JSONModel

JSONModel lets you convert a model object into a NSDictionary as following:
NSDictionary *dict = [myJSONModel toDictionary]
It includes all properties of the model (except optional). However, I also need to create multiple dictionaries having only some model fields required for a particular backend operation.
Some fields could be included in multiple dictionaries, so ideally, it would be awesome if I could do something like:
NSDictionary *dictOne = [myJSONModel dictionaryWithKeyMapper:myJSONMapperOne]
NSDictionary *dictTwo = [myJSONModel dictionaryWithKeyMapper:myJSONMapperTwo]
and it only returns the objects that have been mapped in that particular mapper.
I am sure there's nothing like this at present. The keymapper for every class is created only once and then is cached, so you can't changed it programatically. Plus you cannot ignore properties via the keymapper.
JSONModel is built like so that it supposes you always need to do the same transformations when you convert between JSON and your model, this way it can do the performance optimisations it does.
However "toDictionary" is not too complicated, you could try subclassing JSONModel and playing around with implementing a "toDictionaryWithFieldList" that takes in field names list and exports only those fields ... just an idea

Is it posible to conditionally disable NSValueTransformer for NSManagedObject attribute?

Specifically, say I have an NSManagedObject with a "statusCode" attribute set to transformable, and a reversible value transformer subclass to covert from NSStrings to NSNumbers and vice versa. The idea is to use the value transformer so that I receive JSON and a string from a "status" key in the JSON automatically maps to an NSNumber that represents that status code in an NSManagedObject. Conversely if I were to upload the NSManagedObject to a server, at that point its status attribute would be transformed from an NSNumber to a string for the JSON.
So far so good. But, what if I also want to be able to get a simple int out of the NSManagedObjec's status property, so that I can AND it with enums in code?
That is, I'd lie to cover 3 cases:
myManagedObject.status = [JSONResponse valueForKey:#"status"] (should use transformer to do NSString -> NSNumber)
[JSONforUpload setValue:myManagedObject.status forKey:#"status"] (should use transformer to do NSNumber->NSString)
From elsewhere in code, anything along the lines of: if(myManagedObject.status & statusInProgress) ... where statusInProgress is an enum.
I'm thinking I could temporarily disable the value transformer, however I have no idea if the NSManagedObject has a reference to it, or if I should disable it from the NSValueTransformer class, which apparently keeps a table of registered transformers?
I know that for the 3rd case I could just do [myManagedObject.status intValue] and then do the bitwise comparison, but I'm wondering if there's any way I can have the intValue] be returned automagically, from the user of this object's point of view.
Any ideas?
Why don't you just write two additional methods for the JSON transform and leave the property as integer? Then you'd have the best from both worlds.
One approach would be to add a property to the transformer so that it switches between string and enum reversed values. That would work, though I ended up doing a enum<->string transformer and not using it over a transformable attribute (instead I left the managed object's attribute as int) but rather instantiating it only for the JSON <-> object conversion. After that, throughout code I just use the int attribute as is.
Assuming that this entity has its own distinct managed object subclass, you could also simply add another pair of accessor methods to the class to encapsulate the conversion between NSNumber and int values. (Or add a transient attribute, if it needs to be part of the model. But you'd still need to write custom accessors to synch up the values.)

NSDictionary with NSObjects as keys

I'm trying to create an NSDictionary that keeps track of calling objects for a function. I'd like to create a unique string for each object without knowing anything about it. My first thought is to use the memory address of the object's pointer, but I'm not sure how to do that.
Any thoughts? I need to use some sort of unique id from an NSObject as the keys in my dictionary.
If your application supports iOS6 only check the NSDictionaryOfVariableBindings macro.
The code would be something like :
// Create the dictionary
NSObject *firstObject = [NSString stringWithString:#"My first item"];
NSObject *secondObject = #"[#"an", #"array", #"of", #"strings"]";
NSDictionary *theDic = NSDictionaryOfVariableBindings(firstObject, secondObject);
// Access data
NSString *singleString = [theDic objectForKey:#"firstObject"];
NSArray *listOfStrings = [theDic objectForKey:#"secondObject"];
My suggestion is not to use a dictionary. If you were to place them into an array, you could think of it as a dictionary with automatically generated unique keys (the indexes). It's really exactly what you are describing. If for some reason you have to use a dictionary, my suggestion is to implement that same model I'm speaking of, but you would have to generate and maintain the keys.
While I agree that your solution sounds like it may not be the best approach, have you considered -hash in the NSObject protocol? All NSObjects should return one. Be forewarned that it's a hash, so there's a chance that two different objects could have the same hash.
You could also consider a category on NSObject that your collection implements. The category could generate a UUID to use as a key.

Resources