Using non-copyable object as key for NSMutableDictionary? - objective-c

I tried to figure out this code referencing: Cocoa: Dictionary with enum keys?
+ (NSValue*)valueWithReference:(id)target
{
return [NSValue valueWithBytes:&target objCType:#encode(id*)];
}
And,
[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];
But it feels something not good. Any recommendations?

You're absolutely right it's not good.
For one, you're encoding the wrong type (it should be #encode(id), not #encode(id*)), but in most cases this shouldn't cause a big problem.
The bigger problem is that this completely ignores memory management. The object won't be retained or copied. If some other code releases it, it could just disappear, and then your dictionary key will be a boxed pointer to garbage or even a completely different object. This is basically the world's most advanced dangling pointer.
You have two good options:
You could either add NSCopying to the class or create a copyable subclass.
This option will only work for objects that can meaningfully be copied. This is most classes, but not necessarily all (e.g. it might be bad to have multiple objects representing the same input stream)
Implementing copying can be a pain even for classes where it makes sense — not difficult, per se, but kind of annoying
You could instead create the dictionary with the CFDictionary API. Since Core Foundation types don't have a generic copy function, CFDictionary just retains its keys by default (though you can customize its behavior however you like). But CFDictionary is also toll-free bridged with NSDictionary, which means that you can just cast a CFDictionaryRef to an NSDictionary* (or NSMutableDictionary*) and then treat it like any other NSDictionary.
This means that the object you're using as a key must not change (at least not in a way that affects its hash value) while it's in the dictionary — ensuring this doesn't happen is why NSDictionary normally wants to copy its keys

For the later reference.
Now I know that there are some more options.
Override methods in NSCopying protocol, and return the self instead of copying itself. (you should retain it if you are not using ARC) Also you ensure the object to always return same value for -hash method.
Make a copyable simple container class holds strong reference to the original key object. The container is copyable but, it just passes original key when it being copied. Override equality/hash methods also to match semantics. Even just an instance of NSArray contains only the key object works well.
Method #1 looks pretty safe but actually I'm not sure that's safe. Because I don't know internal behavior of NSDictionary. So I usually use #2 way which is completely safe in Cocoa convention.
Update
Now we Have NSHashTable and NSMapTable also in iOS since version 6.0.

I'm not 100% sure about the correctness of this solution, but I'm posting it just in case.
If you do not want to use a CFDictionary, maybe you could use this simple category:
#implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
[self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}
- (id)objectForNonCopyableKey:(id)aKey {
return [self objectForKey:[NSValue valueWithPointer:aKey]];
}
- (void)removeObjectForNonCopyableKey:(id)aKey {
[self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
#end
This is a generalization of a similar method I saw online (can't find the original source) for using an NSMutableDictionary that can store objects with UITouch keys.
The same restriction as in Chuck's answer applies: the object you're using as a key must not change in a way that affects its hash value and must not be freed while it's in the dictionary .
Also make sure you don't mix -(void)setObject:(id)anObject forNonCopyableKey:(id)aKey and - (id)objectForKey:(id)aKey methods, as it won't work (the latter will return nil).
This seems to work fine, but there might be some unwanted side effects that I am not thinking of. If anybody finds out that this solution has any additional problems or caveats, please comment.

Related

NSUUID as Unique ID as replacement to NSManagedObjectID

I've run into some problems using NSManagedObjectID and it changing depending on it's saved state.
As such I've decided to use my own UniqeIDs as recommended in the docs and by others. I've seen many examples here on Stack Overflow and on other places where it's a simple matter of storing a NSUUID string value in a core data field.
However, this isn't quite enough for what I want. One of the useful things about NSManagedObjectID is that it is always the same object and can be compared by pointer, so you can post notifications around using the NSManagedObjectID as their object, anything that requires information about the entity can register for notification based on the NSManagedObjectID without writing additional code to check if the notification is indeed the one we're looking for.
However, would that be still be true if an NSString is passed around instead of the NSManagedObjectID? We're always supposed to use isEqualTo for NSString comparison, even if it might be the same object. I feel like using an NSString as an object for a notification is a bit of a no no.
In my case, it's pretty much guaranteed be the same object, unless objective c messes around with NSString behind the scenes. the uniqueID is generated once on insertion of an object, and would be passed around unaltered as required, and I simply want to replace all calls where I use NSManagedObjectID with something I can drop in with minimal changes.
A CFUUID would seem ideal, as they can be guaranteed to share pointer values, however CFUuidRef is not an objective-c object, so can't be used for notifications among other things. An NSUUID seems next to best apart from the caveat in the documentation that says they aren't guaranteed to be the same object. But if my NSUUID is created, stored and retrieved on a single object, could we guarantee the passed around NSUUID to be the same object throughout the application? If so, couldn't we say the same thing about an NSString? Even if we could, I'd be happier just going with NSUUID.
I can't pass around the Entity directly, as I'm using the notification to post information between separate threads. Even though I only ever modify the entities on the main thread, and entities can be accessed for readonly across threads, I've had many problems in the past, that all went away once I implemented a system based on using just the NSManagedObjectID.
Maybe I got you wrong, but why not passing around instances of NSUUID (if you do not want to use instances of NSString) and comparing them on equality?
#interface NSUUID(Equality)
- (BOOL)isEqualToUUID:(NSUUID*)other;
#end
#implementation NSUUID(Equality)
- (BOOL)isEqualToUUID:(NSUUID*)other
{
return [self.UUIDString isEqualToString:other.UUIDString];
}
// Only for completness
- (BOOL)isEqual:(id)other
{
if( [other isKindOfClass:[NSUUID class]] )
{
return [self isEqualToUUID:other];
}
return NO;
}
#end
BTW: The same reasons that make you feel badly using instances of NSString as notification object does not apply to instances of NSUUID?
BTW 2: Handling CF-objects in ARC code is not that difficult.

Conflict between memory management descriptions in ObjC book and official docs

I'm trying to learn/understand what happens and why when working with or creating various objects. (Hopefully to LEARN from the docs.)
I'm reading "Programming in Objective-C 2.0" (2nd edition, by Steven Kochan). On page 408, in the first paragraph is a discussion of retain counts:
Note that its reference count then goes to 2. The addObject: method does this automatically; if you check your documentation for the addObject: method, you will see this fact described there.
So I read the addObject: docs:
Inserts a given object at the end of the array.
There, the description is missing, while other items, like arrayByAddingObject:, state it:
Returns a new array that is a copy of the receiving array with a given object added to the end.
Where in the reference does it indicate that addObject: increases the retain count? Given the presence of ARC, I should still understand what these methods are doing to avoid bugs and issues. What does ARC bring to this? (Going to read that again...)
Great question, I'm glad to see someone actually reading the docs and trying to understand them!
Since you are looking for how to research answers using Apple's documentation more so than the actual answer itself, here is how I found the answer:
First I look at the class reference for addObject: which is a method of NSMutableArray and there is no mention of memory management.
Then I look at the Overview section at the top... Hmmm, still no luck.
Since the behavior might be inherited from a parent class, I look at the Inherits from section at the top of the class reference and see that NSArray is the most immediate parent. Let's check there:
Under the Overview There is one small section about retain's:
Special Considerations
In most cases your custom NSArray class should conform to Cocoa’s
object-ownership conventions. Thus you must send retain to each object
that you add to your collection and release to each object that you
remove from the collection. Of course, if the reason for subclassing
NSArray is to implement object-retention behavior different from the
norm (for example, a non-retaining array), then you can ignore this
requirement.
Okay, I'm still not happy... Where next? The parent class of NSArray is NSObject and I know that it won't be covered there in this case (from experience) so I won't bother checking that. (If the parent was another class or something that might be covered by NSObject, I would keep moving up the tree until I found something.)
The Companion Guides usually contains a lot of good information for these types of classes. Let's try the first one, Collections Programming Topics.
The first section (after Overview) is Accessing Indexes and Easily Enumerating Elements: Arrays. Sounds promising! Click on Relevant Chapters: “Arrays: Ordered Collections”
There it is under Array Fundamentals along with a link to even more information:
And when you add an object to an NSMutableArray object, the object
isn’t copied, (unless you pass YES as the argument to
initWithArray:copyItems:). Rather, an object is added directly to an
array. In a managed memory environment, an object receives a retain
message when it’s added; in a garbage collected environment, it is
strongly referenced. When an array is deallocated in a managed memory
environment, each element is sent a release message. For more
information on copying and memory management, see “Copying
Collections.”
The book must be referring to out of date documentation because you are correct it doesn't mention anything about the retain count. It does in fact retain the object though. The way you need to think of it is not in terms of retain counts (which are useless) but rather ownership. Especially so when using ARC.
When you add an object to an NSMutableArray, it is taking ownership of that object (in ARC terminology it has a strong reference to it).
"What does ARC bring to this?"
ARC does nothing different. All ARC does (besides some optimization) is add the same release, retain, and autorelease statements that you would add yourself without using ARC. All you need to care about is that once you add an object to the array, it will live at least as long as the array.
And the arrayByAddingObject: method creates a new NSArray (or NSMutableArray) containing the object you're passing, and keeps a strong reference to the passed object. The actual array object that it creates has no references yet unless you assign it to either an ivar, property, or local variable. What you assign it to determines it's lifespan.
Basically even without ARC, it's best to think of object life-cycles in terms of ownership, ARC just formalizes that. So because of that, when using the frameworks, it doesn't matter when retains happen or don't happen, you are only responsible for your objects until you pass ownership to another object and you can trust that the framework will keep the object alive as long as it needs it.
Now of course you have to intuit what constitutes ownership. For instance delegate properties are often assign, or in ARC unsafe_unretained or weak, to prevent circular retains cycles (where two objects each retain each other), though are sometimes retained/strong so you need to look into those on a case by case basis.
And also in cases like key value observing and NSNotification observing the object you are observing does not retain the observer.
But those are really exceptions to the rule. Generally you can assume a strong reference.
Regarding this sentence above: "The actual array object that it creates has no references yet unless you assign it to either an ivar, property, or local variable. What you assign it to determines it's lifespan." I'll try to explain:
When you run this piece of code: [someArray arrayByAddingObject:someObject]; you've instantiated a new NSArray or NSMutableArray object (depending on which object type someArray is) but you haven't actually assigned it to any reference. That means that if you're using ARC, it may be immediately released afterwards, or if not using ARC, it will be released when it's autoreleasepool is drained (probably on the next iteration of that thread's runloop).
Now if instead you did this: NSArray *someOtherArray = [someArray arrayByAddingObject:someObject]; you now have a reference to the newly created array, called someOtherArray. In this case, this is a local variable who's scope is only within whichever set of { } it resides (so it could be inside an if statement, a loop, or a method. Now if you do nothing else with it, it will die sometime after it's scope ends (it isn't guaranteed to die right away, but that isn't important, you just can't assume it lives longer).
Now if in your class you have an iVar (instance variable) declared in the header like NSArray *someOtherArray; (which is strong by default in ARC) and you run someOtherArray = [someArray arrayByAddingObject:someObject]; somewhere in your class, the object will live until you either remove the reference (someOtherArray = nil), you overwrite the reference (someOtherArray = someThirdArray), or the class is deallocated. If you were not using ARC, you would have to make sure to retain that to achieve the same effect (someOtherArray = [[someArray arrayByAddingObject:someObject] retain]; which is essentially what ARC is doing behind the scenes).
Or you may have a property declared instead like #property (nonatomic, strong) NSArray *someOtherArray in which self.someOtherArray = [someArray arrayByAddingObject:someObject]; would achieve the same effect but would use the proprety accessor (setSomeOtherArray:) or you could still use someOtherArray = [someArray arrayByAddingObject:someObject]; to set the iVar directly (assuming you #synthesized it).
Or assuming non-ARC, you might have declared the property like #property (nonatomic, retain) NSArray *someOtherArray in which self.someOtherArray = [someArray arrayByAddingObject:someObject]; would behave exactly as ARC would, but when setting the iVar directly you would still need to add that retain manually.
I hope that clears things up a bit, please let me know if there's anything I glossed over or left out.
As you mentioned in your comment, the key here is intuitively knowing when an object would be considered owned by another one or not. Luckily, the Cocoa frameworks follow a pretty strict set of conventions that allow you to make safe assumptions:
When setting an NSString property of a framework object (say the text property of a UILabel for example) it is always copied (if anyone knows of a counter-example, please comment or edit). So you don't have to worry about your string once you pass it. Strings are copied to prevent a mutable string from being changed after it's passed.
When setting any other property other than delegate, it's (almost?) always retained (or strong reference in ARC)
When setting delegate properties, it's (almost?) always an assign (or weak reference) to prevent circular retain cycles. (For instance, object a has a property b that is strong referenced and b has a strong referenced delegate property. You set a as the delegate for b. Now a and b are both strongly referencing each other, and neither object will ever reach a retain count of 0 and will never reach it's dealloc method to dealloc the other object. NSURLConnection is a counter-example that does strongly reference it's delegate, because it's delegate is set via a method -- see that convention below -- and it's convention to nil out or release an NSURLConnection after it completes rather than in dealloc, which will remove the circular retain)
When adding to an array or dictionary, it's always retained (or strong reference).
When calling a method and passing block(s), they are always copied to move them from the stack (where they are initially created for performance purposes) into the heap.
Methods that take in object parameters and don't return a result immediately are (always? I can't think of any that don't) either copying or retaining (strong referencing) the parameters that you pass to ensure that the method can do what it needs to with them. For instance, NSURLConnection even retains it's delegate because it's passed in via a method, whereas when setting the delegate property of other objects will not retain, as that is the convention.
It's suggested that you follow these same conventions in your own classes as well for consistency.
Also, don't forget that the headers of all classes are available to you, so you can easily see whether a property is retain or assign (or strong or weak). You can't check what methods do with their parameters, but there's no need because of the convention that parameters are owned by the receiver.
In general, you should look in the "most global" spot for information about anything in the Cocoa APIs. Since memory management is pervasive across the system APIs and the APIs are consistent in their implementation of the Cocoa memory management policy, you simply need to read and understand the Cocoa memory management guide.
Once understood, you can safely assume that all system APIs implement to that memory management policy unless explicitly documented otherwise.
Thus, for NSMutableArray's addObject: method, it would have to retain the object added to the array or else it would be in violation of that standard policy.
You'll see this throughout the documentation. This prevents every method's documentation from being a page or more long and it makes it obvious when the rare method or class implements something that is, for whatever reason (sometimes not so good), an exception to the rule.
In the "Basic Memory Management Rules" section of the memory management guide:
You can take ownership of an object using retain.
A received object is normally guaranteed to remain valid within the
method it was received in, and that method may also safely return the
object to its invoker. You use retain in two situations: (1) In the
implementation of an accessor method or an init method, to take
ownership of an object you want to store as a property value; and (2)
To prevent an object from being invalidated as a side-effect of some
other operation (as explained in “Avoid Causing Deallocation of
Objects You’re Using”).
(2) is the key; an NS{Mutable}Array must retain any added object(s) exactly because it needs to prevent the added object(s) from being invalidated due to some side-effect. To not do so would be divergent from the above rule and, thus, would be explicitly documented.

Can I make an NSInteger an optional parameter on a method?

I would like to have a method along the lines of
setData:(SomeClassName *)data inPosition:(NSInteger)position
and in the implementation, check for nil as position. The idea is that if the position is provided, I will use it, and if not, I will allocate it automatically.
The problem is I can't pass either NULL or nil into this without a compiler warning.
I believe I have seen this pattern elsewhere (optional parameters). I think it might have been related to an NSIndexPath.
Should I use an NSNumber as a wrapper? or is there some other secret?
As an aside, I considered using separate methods - setData: and setData:inPosition:. But the problem is that 'data' is a core data created attribute, not a regular ivar, so when I actually want to set the value I would have to remember to send all the KVO messages. For example, inside setData:withPosition, I can't call the standard setData: - it would overwrite any work I did with the position.
Would also be interested in knowing which is the 'better' solution of these two.
#Justin's approach is generally the most appropriate. However, to your question about setData: and KVO, there are several things to note:
KVO notifications are sent automatically as long as the method is named setFoo:. Even if you override setFoo:, KVO will wrap your implementation with the correct KVO notification calls for the property. This is very likely the most magical thing in Cocoa. (I used to be certain it was the most magical thing, but I'm starting to wonder about block variable scoping, and especially how blocks are moved from the stack to the heap; that may be more magical.)
If you need to set a Core Data attribute directly, bypassing KVO and every other piece of possible magic, you can use the primitive accessor. setPrimitiveData: is the underlying method that setData: uses to set the property. You should not override the primitive accessors.
#Justin appears to have deleted his answer. The typical solution here would be to declare setData: and setData:inPosition: (btw, as a reader, I have no idea what "inPosition" means. I hope that it makes sense in context). setData: would call setData:inPosition: applying whatever is necessary to figure out "position."
Using the NSNumber wrapper is pretty standard.
Of course, you could always pass -1, NSNotFound, or define your own n/a value too.
There are three options:
Pass -1 or some such for "no value"
Use an NSNumber wrapper and pass nil for "no value"
Overload
You could try to use the Objective-C optional parameter mechanism, but that requires some sort of sentinel to mark the end of the list, so it's no better than any of the others.

Is there any reason not to return a mutable object where one is not expected?

I have a number of functions similar to the following:
+ (NSArray *)arrayOfSomething
{
NSMutableArray *array = [NSMutableArray array];
// Add objects to the array
return [[array copy] autorelease];
}
My question is about the last line of this method: is it better to return the mutable object and avoid a copy operation, or to return an immutable copy? Are there any good reasons to avoid returning a mutable object where one is not expected?
(I know that it is legal to return a NSMutableArray since it is a subclass of NSArray. My question is whether or not this is a good idea.)
This is a complex topic. I think it's best to refer you to Apple's guidelines on object mutability.
Apple has this to say on the subject of using introspection to determine a returned object's mutability:
To determine whether it can change a received object, the receiver must rely on the formal type of the return value. If it receives, for instance, an array object typed as immutable, it should not attempt to mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class membership
(my emphasis)
The article goes on to give several very good reasons why you should not use introspection on a returned object to determine if you can mutate it e.g.
You read a property list from a file. When the Foundation framework processes the list it notices that various subsets of the property list are identical, so it creates a set of objects that it shares among all those subsets. Afterwards you look at the created property list objects and decide to mutate one subset. Suddenly, and without being aware of it, you’ve changed the tree in multiple places.
and
You ask NSView for its subviews (subviews method) and it returns an object that is declared to be an NSArray but which could be an NSMutableArray internally. Then you pass that array to some other code that, through introspection, determines it to be mutable and changes it. By changing this array, the code is mutating NSView’s internal data structures.
Given the above, it is perfectly acceptable for you to return the mutable array in your example (provided of course, you never mutate it yourself after having returned it, because then you would be breaking the contract).
Having said that, almost nobody has read that section of the Cocoa Objects Guide, so defensive programming would call for you to make an immutable copy and return that unless performance profiling shows that it is a problem to do that.
Short Answer: Don't do it
Long Answer: It depends. If the array is getting changed while being used by someone who expects it be static, you can cause some baffling errors that would be a pain to track down. It would be better to just do the copy/autorelease like you've done and only come back and revisit the return type of that method if it turns out that there is a significant performance hit.
In response to the comments, I think it's unlikely that returning a mutable array would cause any trouble, but, if it does cause trouble, it could be difficult to track down exactly what the issue is. If making a copy of the mutable array turns out to be a big performance hit, it will be very easy to determine what's causing the problem. You have a choice between two very unlikely issues, one that's easy to solve, one that's very difficult.

How to inherit from NSDictionary?

I have an object called Settings that inherits from NSMutableDictionary. When I try to initialize this object using
Settings *settings = [[Settings alloc] initWithContentsOfFile: #"someFile"]
it returns an object of type NSCFDictionary. As a result, it doesn't recognize my additional methods. For example, when I call the selector "save", it objects:
[NSCFDictionary save]: unrecognized selector sent to instance 0x524bc0
Of course, it's OK when I initialize using the garden variety
Settings *settings = [[Settings alloc] init]
I tried to cast it again to Settings but that didn't work. This seems really simple - what am I missing?
Thanks
NSDictionary is a class cluster. This means that the value returned from its init methods is not strictly an NSDictionary, but a subclass that implements the actual functionality. In almost every case, it is better to give your class an NSDictionary as an instance variable or to simply define a category on NSDictionary.
Chuck is correct about NSDictionary (and Dave, by extension, about NSArray/Set/String) and class clusters. Odds are that -[NSDictionary initWithContentsOfFile:] calls down to a different initializer than -init does, which is why it swaps out your allocated Settings instance for another subclass of NSMutableDictionary. (The initialization action when reading from a file may select a particular known subclass of NSDictionary which performs well for loading from a file, etc.)
I'll echo Chuck's guidance that it is almost always better to use composition or categories than inheritance for an NSDictionary. It's highly likely that you could accomplish what you're doing with categories in a much simpler way, and expose yourself to fewer potential bugs in the process. Consider yourself warned before deciding to subclass.
That being said, both NSDictionary and NSMutableDictionary have been designed to support subclassing, and on rare occasions that's the right thing to do. Think long and hard about it before trying it. If you find it's the right choice for your design, here are some key points to know and do as needed:
Override the following primitive methods from NSDictionary:
-count
-objectForKey:
-keyEnumerator
-initWithObjects:forKeys:count: (designated initializer)
Override the following primitive methods from NSMutableDictionary:
-setObject:forKey:
-removeObjectForKey:
If you're supporting NSCoding, be aware of classForKeyedArchiver and replacementObjectForKeyedArchiver: (both instance methods from NSObject) — they can totally change how your class responds, and you often unintentionally inherit some odd behavior from NS(Mutable)Dictionary. (You can verify if they are the culprit by setting a breakpoint on them, or implementing them to call super and breaking on your own code.)
I've implemented a number of these points in an NSMutableDictionary subclass of my own. You can check it out and use the code however may be helpful to you. One that particularly helped me (and could be the solution for your problem) was overloading the designated initializer, which is currently undocumented (Radar #7046209).
The thing to remember is that even though these bullets cover most common uses, there are always edge cases and less common functionality to account for. For example, -isEqual: and -hash for testing equality, etc.
If you actually read the spec for NSDictionary (a rash action, I know) you'll find a section named "Subclassing Notes". In it you will read:
If you do need to subclass NSDictionary, you need to take into account
that is represented by a Class cluster—there are therefore several
primitive methods upon which the methods are conceptually based:
initWithObjects:forKeys:
count
objectForKey:
keyEnumerator
In a subclass, you must override all these methods.
From https://stackoverflow.com/a/1191351/467588, this is what I did to make a subclass of NSDictionary works. I just declare an NSDictionary as an instance variable of my class and add some more required methods. I don't know what to call them though.
I posted my code sample here https://stackoverflow.com/a/10993594/467588.
This question is very old, and since most of these answers were posted, Apple has introduced object subscripting, which allows you to make your own classes behave more like NSMutableArray or NSMutableDictionary. This is simpler than the alternatives discussed above.
At a minimum, you have to override these methods:
//Array-style
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
//Dictionary-style
- (id)objectForKeyedSubscript:(id <NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
Here's a nice tutorial on how to do just that.