Is there NSMutableDictionary literal syntax to remove an element? - objective-c

There is a literal syntax to add object and change object in an NSMutableDictionary, is there a literal syntax to remove object?

Yes, but... :-)
This is not supported by default, however the new syntax for setting dictionary elements uses the method setObject:forKeyedSubscript: rather than setObject:forKey:. So you can write a category which replaces the former and either sets or removes the element:
#implementation NSMutableDictionary (RemoveWithNil)
- (void) setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key
{
if (obj)
[self setObject:obj forKey:key];
else
[self removeObjectForKey:key];
}
#end
Add that to your application and then:
dict[aKey] = nil;
will remove an element.

No. There is not. I have tried to find proof link but did not succeed :)

As of iOS 9 and macOS 10.11, these two are equivalent:
[dictionary removeObjectForKey:#"key"];
dictionary[#"key"] = nil;
See the Foundation release notes (search for the heading NSMutableDictionary subscript syntax change).

Related

NSSet with NSStrings containstObject not return YES when it should

I'm loading a dictionary (list of word, not the class) into a NSSet as NSStrings. I then repeatedly send this set the message -containsObject:someNSString. But it always returns false. I wrote some code to test it:
NSLog(#"Random from dictionary: %#", [dictionary anyObject]);
NSString *test = [NSString stringWithFormat:#"BEMIRED"];
NSLog(#"To match this word: %#", test);
if ([dictionary containsObject:test])
NSLog(#"YES!");
In the log I get the following:
Random from dictionary: BEMIRED
To match this word: BEMIRED
(I'm missing the "YES!")
When I try using CFShow(dictionary) I can see that it actually contains Strings and that everything. An example:
0 : <CFString 0xc3bd810 [0x1386400]>{contents = "BEMIRED"}
3 : <CFString 0xdf96ef0 [0x1386400]>{contents = "SUBJECTIFIED"}
Can anyone please help me here?
Thanks!
NSSet uses isEqual: to test for object equality, which NSString overrides to perform a string comparison as you would expect. The follow unit test passes:
- (void)testSetStrings
{
NSSet *set = [NSSet setWithObject:#"String 1"];
// I've used the UTF8 initializer to avoid any cleverness from reusing objects
NSString *string1 = [[[NSString alloc] initWithUTF8String:"String 1"] autorelease];
// Test the references/pointers are not the same
STAssertTrue([set anyObject] != string1, nil);
STAssertTrue([set containsObject:string1], nil);
}
We can see the two strings have different pointer values, but the set still returns YES for the containsObject: call.
So I would guess your strings are not in fact equal. I would check for hidden whitespace or other similar issues.
The -[NSSet containsObject:] seems to check for the pointer value only (the documentation is very lacking for that method), not for object equality. So you need to use -[NSSet member:] instead, which uses isEqual: to check whether an object that is considered to be equal is in your set.
if ([dictionary member:test])
NSLog(#"YES!");
Edit: Actually it seems that containsObject: does use isEqual: as well. They only seem to differ in what they return (containsObject: returns a BOOL while member: returns id). I'm letting this answer stay for documentation purposes.
Ok so I solved the problem and it had nothing to do with the containsObject method. As I commented i used Dave DeLongs DDFileReader found here: Dave DeLongs DDFileReader
So by using CFShow on the entire dictionary I noticed that every word had a new line at the end of it. So instead of the -readLine method i used the -readTrimmedLine (bot methods in above mentioned file reader). This solved the problem for me.
For future forum visitors I'd like to draw attention to the discussion DarkDust and zoul had about -containsObject and -member (both methods of NSSet) which it turns out both uses the -isEqual method.

what is this weird code notation mean

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)

Defining your own key path operators in cocoa

Is it possible to define your own key path operators, such as #avg, #sum, etc…
Short answer: Kinda. You can override valueForKeyPath: to intercept your custom operator or forward on to super, but that can be problematic (I'll leave the explanation to that as an exercise to the reader).
Long answer: Yes you can, but it relies on using private behavior (not private api).
After some neat introspection of NSArray, I found some private methods:
_distinctUnionOfSetsForKeyPath:
_distinctUnionOfObjectsForKeyPath:
_distinctUnionOfArraysForKeyPath:
_unionOfSetsForKeyPath:
_unionOfArraysForKeyPath:
_unionOfObjectsForKeyPath:
_minForKeyPath:
_maxForKeyPath:
_countForKeyPath:
_avgForKeyPath:
_sumForKeyPath:
Well, neat! Those methods seem to match the operators you can use with collections: #sum, #min, #max, #distinctUnionOfObjects, etc. The # has been replaced with an underscore and we've got ForKeyPath: appended.
So it would seem that we can create a new method to match the appropriate signature and we're good to go.
So:
#interface NSArray (CustomOperator)
- (id) _fooForKeyPath:(NSString *)keyPath;
#end
#implementation NSArray (CustomOperator)
- (id) _fooForKeyPath:(NSString *)keyPath {
//keyPath will be what comes after the keyPath. In this example, it will be "self"
return #"Hello world!";
}
#end
NSArray * array = [NSArray arrayWithObjects:#"1", #"2", #"3", nil];
NSLog(#"%#", [array valueForKeyPath:#"#foo.SELF"]); //logs "Hello world!"
It works, but I'm not sure I would rely on this, since it relies on an implementation detail that could change in the future.
It's possible by overriding valueForKeyPath: and doing your own custom logic in there, but there's no framework support for it.

If I want to make a new instance of an object in a function whose pointer is passed by reference in it

- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)

How to view contents of NSDictionary variable in Xcode debugger?

Is there a way to view the key/value pairs of a NSDictionary variable through the Xcode debugger? Here's the extent of information when it is fully expanded in the variable window:
Variable Value Summary
jsonDict 0x45c540 4 key/value pairs
NSObject {...}
isa 0xa06e0720
I was expecting it to show me each element of the dictionary (similar to an array variable).
In the gdb window you can use po to inspect the object.
given:
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"foo" forKey:#"bar"];
[dict setObject:#"fiz" forKey:#"buz"];
setting a breakpoint after the objects are added you can inspect what is in the dictionary
(gdb) po dict
{
bar = foo;
buz = fiz;
}
Of course these are NSString objects that print nicely. YMMV with other complex objects.
You can right-click any object (ObjC or Core Foundation) variable and select “Print Description to Console” (also in Run->Variables View). This prints the result the obejct’s -debugDescription method, which by default calls -description. Unfortunately, NSDictionary overrides this to produce a bunch of internal data the you generally don’t care about, so in this specific case craigb’s solution is better.
The displayed keys and values also use -description, so if you want useful information about your objects in collections and elsewhere, overriding -description is a must. I generally implement it along these lines, to match the format of the default NSObject implementation:
-(NSString *) description
{
return [NSString stringWithFormat:#"<%# %p>{foo: %#}", [self class], self, [self foo]];
}
You can use CFShow()
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"foo" forKey:#"bar"];
[dict setObject:#"fiz" forKey:#"buz"];
CFShow(dict);
In output you will see
{
bar = foo;
buz = fiz;
}
XCode 4.6 has added the following functionality which may be helpful to you
The elements of NSArray and NSDictionary objects can now be inspected in the Xcode debugger
Now you can inspect these object types without having to print the entire object in the console. Enjoy!
Source: http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/WhatsNewXcode/Articles/xcode_4_6.html
Click on your dict, then click on the little "i" icon, it should do the job :-)
If you would like to print these in a breakpoint action in modern XCode (yes, I am 10 years after the original post!) use the following breakpoint expression in a "Log Message" action:
#myDictionary.description#
Below is a screenshot of my breakpoint action where the variable event is an NSString and the variable contextData is the NSDictionary that I am logging the contents of:
:
You can also use NSLog.
Also you can go in Debug area or xcode, then find out All Variables, Registers, Globals and Statics then select your variable. Right click on it. Then select Print description of "...."
Hope it helps!