NSMutableDictionary much slower than Java Map... why? - objective-c

The following code, which maps simple value holders to an object, runs over 15x faster in Java than Objective-C using XCode 7 beta3, "Fastest, Aggressive Optimizations [-Ofast]". I can get over 280M lookups/sec in Java but only about 19M in the objc example. (I posted the corresponding Java code here as this started as a Swift comparison: Swift Dictionary slow even with optimizations: doing uncessary retain/release?).
This is a simplified version of my real code which is definitely bound by hash lookup time and exhibits this overall performance difference as well. In the test below I'm testing the value for null just to make sure the compiler doesn't optimize away the lookup, but in the real app I'd be using the value in most cases.
When I look at instruments I see a lot of time spent in retain / release, msgSend, and some locking calls that I don't understand.
Any ideas on what could account for this being 10-15x slower than Java or any workarounds would be appreciated. I can actually implement a perfect hash like the one below so I could use a fast int-object dictionary for iOS if I could find one.
#interface MyKey : NSObject <NSCopying>
#property int xi;
#end
#implementation MyKey
- (NSUInteger)hash { return self.xi; }
- (BOOL)isEqual:(id)object { return ((MyKey *)object).xi == self.xi; }
- (id)copyWithZone:(NSZone *)zone { return self; }
#end
NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:2501];
NSObject *obj = [[NSObject alloc] init];
int range = 2500;
for (int x=0; x<range; x++) {
MyKey *key = [[MyKey alloc] init];
key.xi=x;
[map setObject:obj forKey:key];
}
MyKey *key = [[MyKey alloc] init];
int runs = 50;
for (int run=0; run<runs; run++)
{
NSDate *start = [NSDate date];
int reps = 10000;
for(int rep=0; rep<reps; rep++)
{
for (int x=0; x<range; x++) {
key.xi=x;
if ( [map objectForKey:key] == nil ) { NSLog(#"missing key"); }
}
}
NSLog(#"rate = %f", reps*range/[[NSDate date] timeIntervalSinceDate:start]);
}

You could reimplement your -isEqual: method like this to avoid property accessors:
- (BOOL) isEqual:(id)other
{
return _xi == ((MyKey*)other)->_xi;
}
That would not be acceptable if your MyKey class might be subclassed, but I see from the Java code that the class there is final.

The computational complexity of the NSMutableDictionary is the next (from CFDictionary.h file):
The access time for a value in the dictionary is guaranteed to be at
worst O(N) for any implementation, current and future, but will
often be O(1) (constant time). Insertion or deletion operations
will typically be constant time as well, but are O(N*N) in the
worst case in some implementations. Access of values through a key
is faster than accessing values directly (if there are any such
operations). Dictionaries will tend to use significantly more memory
than a array with the same number of values.
Means, almost all the time you should have O(1) complexity for access/insertion/deletion. For Java HashMap you should get pretty much the same.
According to this research there are no benefits in using dictionaryWithCapacity: convenience initializer.
In case you use integer as a key, probably it would be possible to replace dictionary with array.
In this WWDC session they explained objc_msgSend performance issues and how to deal with them.
The first solution is to use C++ and STL containers. The second one is to use Swift, because unlike Objective-C it is only dynamic when it notes to be.

Related

How to replicate NSArray memory semantics in a subclass

Question
In my ARC project I have a class that manages objects, called LazyMutableArray. Some of the objects are actually nil, but users of my collection will never know about this; therefore I made it a subclass of NSMutableArray, and it tries to do "the same thing". In particular, objects are retained when added.
Now let's take a look at a memory behavior of other methods. It turns out that the NSArray destruction methods are documented by Apple to be an exception to this rule, in that they release, not autoreleased object.
There is some debate as to whether the combination of addObject: + objectAtIndex: + array destruction is documented by Apple to be never autoreleasing or simply happens to be in the examples I tested and in the example Apple includes.
How can I create in my subclass a method with exact same memory semantics?
Last update
After some thought, I've decided implementation based on NSMutableArray is more appropriate in this case compared to NSPointerArray. The new class, I should note, has the same retain/autorelease pair as the previous implementation.
Thanks to Rob Napier I see that no modification of my objectAtIndex: method would change this behavior, which answers my original question about this method.
On a practical level, several people said that any method can tackle an extra retain/autorelease pair for no reason; it's not reasonable to expect otherwise and not reasonable to try to find out which methods do this and which do not. It's been therefore a great learning opportunity for me on several levels.
Code (based on NSMutableArray) is available at GitHub: implementation, header, test (that's -testLazyMutableMemorySemantics).
Thank you all for participating.
Why I try to subclass NSMutableArray:
Subclassing foundation objects, I agree, is not always an appropriate solution. In tho case I have objects (in fact, OData resources), most of which have subobjects. The most natural class for an array of subobjects is obviously NSArray. Using a different class doesn't seem to make sense to me.
But for an OData collection this "array of sub objects", while, being an NSArray, must have a different implementation. Specifically, for a collection of 1000 elements, servers are encouraged to return collection in batches of (say)20, instead of all at once. If there is another pattern appropriate in this case, I'm all ears.
Some more detail in how I found this
I unit test the hell out of this collection, and values can be put into array, read from the array, and so forth. So far, so good. However, I realized that returning the object increases its retain count.
How do I see it? Suppose I insert two objects into lazy array lazy, one held weakly, one held strongly (*see the code *). Then retain count of weakSingleton is, as expected, 1. But now I read element:
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage"); // line B
And in the debugger I see the retain count go up to 2. Of course, -retainCount may give me wrong information, so let's try to destroy the reference in array by
lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, #"Dropped by lazy array"); // line C <-- FAIL
indeed, we see that weakSingleton is not released.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. The exact source of this pair is not obvious, but seems to come from NSPointerArray -addPointer: (and unfortunately not from ARC's [[object retain] autorelease]). However, I don't want to return an autoreleased object and make method semantics different from its superclass!
After all, the method I'm overriding, NSMutableArray -objectAtIndex:`, doesn't do that; the object it returns will dealloc immediately if an array is released, as noted in the Apple's example. That's what I want: modify the method around line A so that the object it returns does not have an extra retain/autorelease pair. I'm not sure the compiler should even let me do it :)
Note 1 I could turn off ARC for a single file, but this would be my first non-ARC Objective-C code. And in any case the behavior may not some from ARC.
Note 2 What the fuss? Well, in this case I could change my unit tests, but still, the fact is that by adding or deleting line B, I'm changing the result of unit test at line C.
In other words, the described behavior of my method [LazyMutableArray -objectAtIndex] is essentially that by reading an object at index 0, I'm actually changing the retain count of this object, which means I could encounter unexpected bugs.
Note 3 Of course, if nothing is to be done about this, I'll document this behavior and move on; perhaps, this indeed should be considered an implementation detail, not to be included into tests.
Relevant methods from implementation
#implementation LazyMutableArray {
NSPointerArray *_objects;
// Created lazily, only on -setCount:, insert/add object.
}
- (id)objectAtIndex:(NSUInteger)index {
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
// otherwise do something else to compute a return value
// but this branch is never called in this test
[self.delegate array:self missingObjectAtIndex:index];
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
#throw([NSException exceptionWithName:NSObjectNotAvailableException
reason:#"Delegate was not able to provide a non-nil element to a lazy array"
userInfo:nil]);
}
- (void)createObjects {
if (!_objects) {
_objects = [NSPointerArray strongObjectsPointerArray];
}
}
- (void)addObject:(id)anObject {
[self createObjects];
[_objects addPointer:(__bridge void*)anObject];
}
The complete test code:
// Insert two objects into lazy array, one held weakly, one held strongly.
NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];
__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];
XCTAssertNotNil(weakSingleton, #"Held by lazy array");
XCTAssertTrue(lazy.count == 2, #"Cleaning and adding objects");
// #autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage");
XCTAssertEqual(singleton, lazy[1], #"Correct element storage");
// }
lazy = nil;
XCTAssertNotNil(singleton, #"Not dropped by lazy array");
XCTAssertNil(weakSingleton, #"Dropped by lazy array");
The last line fails, but it succeeds if I change first line to lazy = [NSMutableArray new] or if I uncomment #autoreleasepool.
First, I would not make this subclass. This is exactly what NSPointerArray is for. Wrapping that into an NSArray obscures important details that this approach can break. For example, what is the correct behavior for [NSArray arrayWithArray:lazyMutableArray] if lazyMutableArray includes NULLs? Algorithms that assume that NSArray can never include NULL need to be wary of the fact that this one can. It's true that you can get similar issues treating a non-retaining CFArray as an NSArray; I speak from experience that this is exactly why this kind of subclass can be very dangerous (and why I stopped doing that years ago). Don't create a subclass that cannot be used in every case that its superclass can be used (LSP).
If you have a collection with new semantics, I would subclass it from NSObject, and have it conform to <NSFastEnumeration>. See how NSPointerArray is not a subclass of NSArray. This was not an accident. Faced with the same problem, note the direction Apple chose.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. This seems to be because line A under ARC translates to [[object retain] autorelease]. However, I don't want to return an autoreleased object and make caller remember this!
The caller should never assume anything else. The caller is never free to assume that a method does not add balanced autoreleases. If a caller wants the autorelease pool to drain, that is their responsibility.
All that said, there is some benefit to avoiding an extra autorelease if it's not required, and it's an interesting learning opportunity.
I would start by reducing this code to the simplest form, without your subclass at all. Just explore how NSPointerArray works:
__weak id weakobject;
#autoreleasepool
{
NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
{
id object = [NSObject new];
[parray addPointer:(__bridge void*)object];
weakobject = object;
}
parray = nil;
}
NSAssert(!weakobject, #"weakobject still exists");
My structure here (such as the extra nesting block) is designed to try to avoid accidentally creating strong references I don't mean to make.
In my experiments, this fails without the autoreleasepool and succeeds with it. That indicates that the extra retain/autorelease is being added around or by the call to addPointer:, not by ARC modifying your interface.
If you're not using this implementation for addObject:, I'd be interested in digging deeper. It is an interesting question, even if I don't believe you should be subclassing this way.
I'm going to elaborate on why I said this "looks a lot like a homework assignment." This will likely earn me many down votes, but it will also server as a good learning case for others who later find this question.
Subclassing NSMutableArray not a goal of a program. It is a means to achieve something else. If I were to venture a guess, I expect you were trying to create an array that lazily creates the object when they are accessed. There are better ways to do this without dealing with memory management yourself.
Here's an example of how I would implement a lazy loading array.
#interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
#end
#interface LazyMutableArray ( )
#property (nonatomic, copy) id (^creator)(int);
#property (nonatomic, assign) NSUInteger highestSet;
#end
#implementation LazyMutableArray
- (id)initWithCreator:(id(^)(int))creator
{
self = [super init];
if (self) {
self.highestSet = NSNotFound;
self.creator = creator;
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
id obj = nil;
if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
obj = [super objectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
obj = self.creator(index);
[super replaceObjectAtIndex:index withObject:obj];
}
} else {
if (self.highestSet == NSNotFound) {
self.highestSet = 0;
}
while (self.highestSet < index) {
[super add:[NSNull null]];
self.highestSet += 1;
}
obj = self.creator(index);
[super add:obj];
self.highestSet += 1;
}
return obj;
}
Fair Warning: I'm not compiling or syntax checking any of this code. It probably has a few bugs in it, but it should generally work. Additionally, this implementation is missing an implementation of add:, count, removeObjectAtIndex:, insertObject:atIndex:, and possibly replaceObjectAtIndex:withObject:. What I show here is just to get you started.

Some questions about objective-c block and copy

I write some code use objective-c block, but the result confused me.
#interface MyTest : NSObject
#end
#implementation MyTest
- (void)test {
NSArray *array = [self array1]; // ok
// NSArray *array = [self array2];// crash
// NSArray *array = [self array3];// ok again
dispatch_block_t block0 = (dispatch_block_t)[array objectAtIndex:0];
block0();
dispatch_block_t block1 = (dispatch_block_t)[array objectAtIndex:1];
block1();
}
- (NSArray *)array1 {
int a = 10;
NSMutableArray *array = [NSMutableArray array];
[array addObject:^{
NSLog(#"block0: a is %d", a);
}];
[array addObject:^{
NSLog(#"block1: a is %d", a);
}];
return array;
}
- (NSArray *)array2 {
int a = 10;
return [NSArray arrayWithObjects:^{
NSLog(#"block0: a is %d", a);
}, ^{
NSLog(#"block1: a is %d", a);
}, nil];
}
- (NSArray *)array3 {
int a = 10;
return [NSArray arrayWithObjects:^{
NSLog(#"block0: a is %d", a);
},[^{
NSLog(#"block0: a is %d", a);
} copy], nil];
}
#end
I am confused about:
why array2 crash? what's the REAL difference between array1 and array2 ?
I read some article said block copy will move a block from stack to heap, but in method array1, I do not copy it and it still works. in array3 I just copy the second block it become ok. why ?
where I must use copy when I use block?
BTW, I run the code in Xcode 4.6, under ARC. Thanks
You seem to have found a case of type loss in relation to blocks which the compiler does not handle. But we need to start at the beginning...
The following relates to the use of blocks under ARC. Other scenarios (MRC, GC) are not considered.
That some blocks are created on the stack rather than the heap is an optimisation that could technically be implemented in such a way that programmers never need to be aware of it. However when blocks were first introduced the decision was made that the optimisation would not be transparent to the user, hence the introduction of blockCopy(). Since that time both the specification and the compiler have evolved (and the compiler actually goes beyond the spec), and blockCopy() is not (by the specification) needed it places it used to be, and may not (as the compiler may exceed the spec) be needed in others.
How can the optimisation be implemented transparently?
Consider:
The compiler knows when it creates a stack allocated block; and
The compiler knows when it assigns such a block to another variable; so
Can the compiler figure out for each assignment whether the block needs to be moved to the heap?
The trivial answer is "yes" - move to the heap on any assignment. But that would negate the whole purpose of the optimisation - create a stack block, pass it to another method, which involves and assignment to the parameter...
The easy answer is "don't try" - introduce blockCopy() and let the programmer figure it out.
The better answer is "yes" - but do it smartly. In pseudo-code the cases are:
// stack allocated block in "a", consider assignment "b = a"
if ( b has a longer lifetime than a )
{
// case 1: assigning "up" the stack, to a global, into the heap
// a will die before b so we need to copy
b = heap copy of a;
}
else
{
if (b has a block type)
{
// case 2: assigning "down" the stack - the raison d'être for this optimisation
// b has shorter life (nested) lifetime and is explicitly typed as a block so
// can accept a stack allocated block (which will in turn be handled by this
// algorithm when it is used)
b = a;
}
else
{
// case 3: type loss - e.g. b has type id
// as the fact that the value is a block is being lost (in a static sense)
// the block must be moved to the heap
b = heap copy of a;
}
}
At the introduction of blocks cases 1 & 3 required the manual insertion of blockCopy(), and case 2 was where the optimisation paid off.
However as explain in an earlier answer the specification now covers case 1, while the compiler appeared to cover case 3 but no documentation confirming that was known.
(BTW if you follow that link you will see it contains a link to an older question on this topic. The case described there is now handled automatically, it is an example of case 1 above.)
Phew, got all that? Let's get back to the examples in the question:
array1, array3 and array4 are all examples of case 3 where there is type loss. They are also the scenario tested in the previous question and found to be handled by the current compiler. That they work is not an accident or luck, the compiler inserts the required block copies explicitly. However I don't know this is officially documented anywhere.
array2 is also an example of case 3 where there is type loss, but it is a variation not tested in the previous question - type loss by passing as a part of a variable argument list. This case does not appear to be handled by the current compiler. So now we have a clue as to why handling of case 3 is not documented - the handling is not complete.
Note that, as mentioned previously, it is possible to test what your compiler does - you can even incorporate some simple tests in your code to immediately abort an application if the tests fail. So you can, if you wish, write code based on what you know the compiler currently handles automatically (so far everything considered accept variadic functions) and which will abort your code should you update the compiler and the replacement lacks the support.
Hope this was helpful and makes sense!
All three of these crash for me (although I suspect the lack of a copy on the first element of array3 is probably an oversight.) A block has to be copied if you want it to outlive the scope in which it was created. Unless you specifically know that a method copies the object you pass into it, you need to copy it yourself.
I tried a fourth case that also works just fine:
- (NSArray *)array4 {
int a = 10;
return #[ ^{
NSLog(#"block0: a is %d", a);
}, ^{
NSLog(#"block1: a is %d", a);
}
];
}
Of course this is the same as:
- (NSArray *)array4 {
int a = 10;
id blocks[] = { ^{
NSLog(#"block0: a is %d", a);
}, ^{
NSLog(#"block1: a is %d", a);
}
};
NSUInteger count = sizeof(blocks) / sizeof(id);
return [NSArray arrayWithObjects:blocks count:count];
}
So the only issue is with "array2". The key point with that implementation is that you are calling the arrayWithObject: method which takes a variable number of arguments.
It seems that only the first (named) argument is properly copied. None of the variable arguments are copied. If you add a third block the problem still arises on the 2nd block. Only the first block is copied.
So it seems that using the blocks with the variable argument constructor, only the first named argument is actually copied. None of the variable arguments are copied.
In all other approaches to creating the array, each block is copied.
BTW - I ran your code and my additions using Xcode 4.6.2 using a simple OS X app under Lion (10.7.5) using ARC. I get identical results when the same code is used in an iOS 6.1 app.

Do loops and convenience methods cause memory peaks with ARC?

I'm working with ARC and seeing some strange behavior when modifying strings in a loop.
In my situation, I'm looping using NSXMLParser delegate callbacks, but I see the same exact behavior and symptoms using a demo project and sample code which simply modifies some NSString objects.
You can download the demo project from GitHub, just uncomment one of the four method calls in the main view controller's viewDidLoad method to test the different behaviors.
For simplicity's sake, here's a simple loop which I've stuck into an empty single-view application. I pasted this code directly into the viewDidLoad method. It runs before the view appears, so the screen is black until the loop finishes.
NSString *text;
for (NSInteger i = 0; i < 600000000; i++) {
NSString *newText = [text stringByAppendingString:#" Hello"];
if (text) {
text = newText;
}else{
text = #"";
}
}
The following code also keeps eating memory until the loop completes:
NSString *text;
for (NSInteger i = 0; i < 600000000; i++) {
if (text) {
text = [text stringByAppendingString:#" Hello"];
}else{
text = #"";
}
}
Here's what these two loops loop like in Instruments, with the Allocations tool running:
See? Gradual and steady memory usage, until a whole bunch of memory warnings and then the app dies, naturally.
Next, I've tried something a little different. I used an instance of NSMutableString, like so:
NSMutableString *text;
for (NSInteger i = 0; i < 600000000; i++) {
if (text) {
[text appendString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
This code seems to perform a lot better, but still crashes. Here's what that looks like:
Next, I tried this on a smaller dataset, to see if either loop can survive the build up long enough to finish. Here's the NSString version:
NSString *text;
for (NSInteger i = 0; i < 1000000; i++) {
if (text) {
text = [text stringByAppendingString:#" Hello"];
}else{
text = #"";
}
}
It crashes as well, and the resultant memory graph looks similar to the first one generated using this code:
Using NSMutableString, the same million-iteration loop not only succeeds, but it does in a lot less time. Here's the code:
NSMutableString *text;
for (NSInteger i = 0; i < 1000000; i++) {
if (text) {
[text appendString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
And have a look at the memory usage graph:
The short spike in the beginning is the memory usage incurred by the loop. Remember when I noted that seemingly irrelevant fact that the screen is black during the processing of the loop, because I run it in viewDidLoad? Immediately after that spike, the view appears. So it appears that not only do NSMutableStrings handle memory more efficiently in this scenario, but they're also much faster. Fascinating.
Now, back to my actual scenario... I'm using NSXMLParser to parse out the results of an API call. I've created Objective-C objects to match my XML response structure. So, consider for example, an XML response looking something like this:
<person>
<firstname>John</firstname>
<lastname>Doe</lastname>
</person>
My object would look like this:
#interface Person : NSObject
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
#end
Now, in my NSXMLParser delegate, I'd go ahead and loop through my XML, and I'd keep track of the current element (I don't need a full hierarchy representation since my data is rather flat, it's a dump of a MSSQL database as XML) and then in the foundCharacters method, I'd run something like this:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if((currentProperty is EqualToString:#"firstname"]){
self.workingPerson.firstname = [self.workingPerson.firstname stringByAppendingString:string];
}
}
This code is much like the first code. I'm effectively looping through the XML using NSXMLParser, so if I were to log all of my method calls, I'd see something like this:
parserDidStartDocument:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didStartElement:namespaceURI:qualifiedName:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didStartElement:namespaceURI:qualifiedName:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didStartElement:namespaceURI:qualifiedName:
parserDidEndDocument:
See the pattern? It's a loop. Note that it's possible to have multiple consecutive calls to parser:foundCharacters: as well, which is why we append the property to previous values.
To wrap it up, there are two problems here. First of all, memory build up in any sort of loop seems to crash the app. Second, using NSMutableString with properties is not so elegant, and I'm not even sure that it's working as intended.
In general, is there a way to overcome this memory buildup while looping through strings using ARC? Is there something specific to NSXMLParser that I can do?
Edit:
Initial tests indicate that even using a second #autoreleasepool{...} doesn't seem to fix the issue.
The objects have to go somewhere in memory while thwy exist, and they're still there until the end of the runloop, when the autorelease pools can drain.
This doesn't fix anything in the strings situation as far as NSXMLParser goes, it might, because the loop is spread across method calls - need to test further.
(Note that I call this a memory peak, because in theory, ARC will clean up memory at some point, just not until after it peaks out. Nothing is actually leaking, but it's having the same effect.)
Edit 2:
Sticking the autorelease pool inside of the loop has some interesting effects. It seems to almost mitigate the buildup when appending to an NSString object:
NSString *text;
for (NSInteger i = 0; i < 600000000; i++) {
#autoreleasepool {
if (text) {
text = [text stringByAppendingString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
}
The Allocations trace looks like so:
I do notice a gradual buildup of memory over time, but it's to the tune of about a 150 kilobytes, not the 350 megabytes seen earlier. However, this code, using NSMutableString behaves the same as it did without the autorelease pool:
NSMutableString *text;
for (NSInteger i = 0; i < 600000000; i++) {
#autoreleasepool {
if (text) {
[text appendString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
}
And the Allocations trace:
It would appear that NSMutableString is apparently immune to the autorelease pool. I'm not sure why, but at first guess, I'd tie this in with what we saw earlier, that NSMutableString can handle about a million iterations on its own, whereas NSString cannot.
So, what's the correct way of resolving this?
You are polluting the autorelease pool with tons and tons of autoreleased objects.
Surround the internal part of the loop with an autorelease pool:
for (...) {
#autoreleasepool {
... your test code here ....
}
}
While you're hunting memory-related bugs, you should note that #"" and #" Hello" will be immortal objects. You can think about it as a const, but for objects. There will be one, and only one, instance of this object in memory the entire time.
As #bbum pointed out, and you verified, the #autoreleasepool is the correct way to deal with this in a loop.
In your example with the #autoreleasepool and NSMutableString, the pool doesn't really do much. The only mortal object inside the loop is your mutableCopy of #"", but that will only be used once. The other case is just an objc_msgSend to a persisting object (the NSMutableString), which only references an immortal object and a selector.
I can only assume the memory build up is inside Apple's implementation of NSMutableString, although I can wonder why you'd see it inside the #autoreleasepool and not when it's absent.

Is there an Objective-C algorithm like `transform` of the C++ STL?

My goal is to have an array that contains all filenames of a specific extension, but without the extension.
There's an elegant solution to get all filenames of a specific extension using a predicate filter and instructions on how to split a path into filename and extension, but to combine them I would have to write a loop (not terrible, but not elegant either).
Is there a way with Objective-C (may be similar to the predicate mechanism) to apply some function to every element of an array and put the results in a second array, like the transform algorithm of the C++ STL does?
What I'd like to write:
// let's pretend 'anArray' was filled by querying the filesystem and not hardcoded
NSArray* anArray = [[NSArray alloc] initWithObjects:#"one.ext", #"two.ext", nil];
// that's what I liked to write (pseudo code)
NSArray* transformed = [anArray transform: stringByDeletingPathExtension];
// Yuji's answer below proposes this (which may be as close as you can get
// to my wish with Objective C)
NSArray* transformed = [anArray my_arrayByApplyingBlock:^(id x){
return [x stringByDeletingPathExtension];
}];
Actually, there is a very simple way. It's been around since 2003 and it is poorly named.
NSArray *array = [NSArray arrayWithObjects:#"one.ext", #"two.ext", nil];
// short solution
NSArray *transformed = [array valueForKey:#"stringByDeletingPathExtension"];
// long solution (more robust against refactoring)
NSString *key = NSStringFromSelector(#selector(stringByDeletingPathExtension));
NSArray *transformed = [array valueForKey:key];
Both produce the output:
(
one,
two
)
That's a topic called Higher Order Messaging in Cocoa, and developed by many people on the web. Start from here and try googling more. They add a category method to NSArray so that you can do
NSArray*transformed=[[anArray map] stringByDeletingPathExtension];
The idea is as follows:
[anArray map] creates a temporary object (say hom)
hom receives the message stringByDeletingPathExtension
hom re-sends the message to all the elements of anArray
hom collects the results and returns the resulting array.
If you just want a quick transform, I would define a category method:
#interface NSArray (myTransformingAddition)
-(NSArray*)my_arrayByApplyingBlock:(id(^)(id))block;
#end
#implementation NSArray (myTransformingAddition)
-(NSArray*)my_arrayByApplyingBlock:(id(^)(id))block{
NSMutableArray*result=[NSMutableArray array];
for(id x in self){
[result addObject:block(x)];
}
return result;
}
#end
Then you can do
NSArray* transformed=[anArray my_arrayByApplyingBlock:^id(id x){return [x stringByDeletingPathExtension];}];
Note the construct ^ return-type (arguments) { ...} which creates a block. The return-type can be omitted, and clang is quite smart on guessing it, but gcc is quite strict about it and needs to be specified sometime. (In this case, it's guessed from the return statement which has [x stringBy...] which returns an NSString*. So GCC guesses the return type of the block to be NSString* instead of id, which GCC thinks is incompatible, thus comes the error. )
On OS X Leopard or iOS 3, you can use PLBlocks to support blocks. My personal subjective opinion is that people who care about new software typically upgrade to the newest OS, so supporting the latest OS should be just fine; supporting an older OS won't increase your customer by a factor of two...
THAT SAID, there's already a nice open-source framework which does all I said above. See the discussion here, and especially the FunctionalKit linked there.
More addition: it's in fact easy to realize your pseudocode [array transform:stringByDeletingPathExtension].
#interface NSArray (myTransformingAddition)
-(NSArray*)my_transformUsingSelector:(SEL)sel;
#end
#implementation NSArray (myTransformingAddition)
-(NSArray*)my_transformUsingSelector:(SEL)sel;{
NSMutableArray*result=[NSMutableArray array];
for(id x in self){
[result addObject:[x performSelector:sel withObject:nil]];
}
return result;
}
#end
Then you can use it as follows:
NSArray*transformed=[array my_transformUsingSelector:#selector(stringByDeletingPathExtension)];
However I don't like it so much; you need to have a method already defined on the object in the array to use this method. For example, if NSString doesn't have the operation what you want to do as a method, what would you do in this case? You need to first add it to NSString via a category:
#interface NSString (myHack)
-(NSString*)my_NiceTransformation;
#end
#implementation NSString (myHack)
-(NSString*)my_NiceTransformation{
... computes the return value from self ...
return something;
}
#end
Then you can use
NSArray*transformed=[array my_transformUsingSelector:#selector(my_NiceTransformation)];
But it tends to be very verbose, because you need to define the method in other places first. I prefer providing what I want to operate directly at the call site, as in
NSArray*transformed=[array my_arrayByApplyingBlock:^id(id x){
... computes the return value from x ...
return something;
}];
Finally, never add category methods which do not start with a prefix like my_ or whatever. For example, in the future Apple might provide a nice method called transform which does exactly what you want. But if you have a method called transform in the category already, that will lead to an undefined behavior. In fact, it can happen that there is a private method by Apple already in the class.

How to store a NSUInteger using NSCoding?

How do I store a NSUInteger using the NSCoding protocol, given that there is no method on NSCoder like -encodeUnsignedInteger:(NSUInteger)anInt forKey:(NSString *)aKey like there is for NSInteger?
The following works, but is this the best way to do this? This does needlessly create objects.
#interface MYObject : NSObject <NSCoding> {
NSUInteger count;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:#"count"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
count = [[decoder decodeObjectForKey:#"count"] unsignedIntegerValue];
}
return self;
}
NSNumber has a lot of methods to store/retrieve different sized and signed types. It is the easiest solution to use and doesn't require any byte management like other answers suggest.
Here is the types according to Apple documentation on NSNumber:
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value
- (NSUInteger)unsignedIntegerValue
Yes your code example is the best way to encode/decode the NSUInteger. I would recommend using constants for the key values, so you don't mistype them and introduce archiving bugs.
static NSString * const kCountKey = #"CountKey";
#interface MyObject : NSObject <NSCoding> {
NSUInteger count;
}
#end
#implementation MyObject
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:kCountKey];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
count = [[decoder decodeObjectForKey:kCountKey] unsignedIntegerValue];
}
return self;
}
#end
The problem is that NSUInteger may not be the same size as an unsigned int. On many (if not most by now) Mac OS X machines, NSUInteger will be an unsigned long, which will be twice as big. On the other hand, a keyed archiver should handle that without a problem, since it builds a dictionary.
Further complicating matters is the fact that NSCoder doesn't have any methods to deal with unsigned types. I can't think of how this would cause any data loss, but it would require some ugly casts, plus it just feels dirty.
If you want to insist upon an unsigned type, the simplest way would be to encode the raw bytes (preferably in network byte order using htonl and ntohl) in the largest type available (unsigned long long) using encodeBytes:length:forKey: and decodeBytesForKey:returnedLength:. For maximum safety, you should check the length of what you decoded and cast the pointer (or use a union) to extract the correct-sized type.
The drawback of this is that the value will be represented in the output as data, not an integer. This mainly matters only if somebody decides to read in the raw plist data for your archive instead of using the keyed unarchiver like you do, and even then only to them. The other cases where it might matter is if Apple (or you) should ever switch to an architecture that has even larger integer types, types whose size in bits is not a power of two (there's at least one old platform where a word was 24 bits), or types with an unusual layout (not big- or little-endian).
As for your NSNumber solution: You might want to crack open your archive in Property List Editor and see what it emitted. If the output contains an integer element, then it's the same as using encodeInteger:forKey:. If the output contains a data element, then it's the same as the solution I mentioned above. To be thorough, you should check the output from every architecture you support.
That is the best way to do it. It does seem like needless work, but there's no other type available that can portably represent the entire range of values NSUInteger can hold.
You're going to want to use NSCoder's
encodeInt:ForKey:
and
decodeIntForKey:
methods. So, in your case you would need :
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeInt:count forKey:#"count"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
count = [decoder decodeIntForKey:#"count"];
}
return self;
}
Hope that helps, also, there's plenty more encode "encode" methods, take a look at the documentation for NSCoder :D