Some questions about objective-c block and copy - objective-c

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.

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.

Objective-C block "retain cycle" warning, don't understand why

I've seen several other questions of the same form, but I either a) can't understand the provided answers, or b) don't see how those situations are similar to mine.
I'm writing a Category on UIView to recursively evaluate all the subviews of a UIView and return an Array of subviews passing a test. I've noted where my compiler warning occurs:
-(NSArray*)subviewsPassingTest:(BOOL(^)(UIView *view, BOOL *stop))test {
__block BOOL *stop = NO;
NSArray*(^__block evaluateAndRecurse)(UIView*);
evaluateAndRecurse = ^NSArray*(UIView *view) {
NSMutableArray *myPassedChildren = [[NSMutableArray alloc] init];
for (UIView *subview in [view subviews]) {
BOOL passes = test(subview, stop);
if (passes) [myPassedChildren addObject:subview];
if (stop) return myPassedChildren;
[myPassedChildren addObjectsFromArray:evaluateAndRecurse(subview)];
// ^^^^ Compiler warning here ^^^^^
// "Capturing 'evaluateAndRecurse' strongly in this block
// is likely to lead to a retrain cycle"
}
return myPassedChildren;
};
return evaluateAndRecurse(self);
}
Also, I get a bad_access failure when I don't include the __block modifier in my block's declaration (^__block evaluateAndRecurse). If someone could explain why that is, that would be very helpful too. Thanks!
The problem here is that your block evaluteAndRecurse() captures itself, which means that, if it's ever to be copied (I don't believe it will in your case, but in slightly less-trivial cases it may), then it will retain itself and therefore live forever, as there is nothing to break the retain cycle.
Edit: Ramy Al Zuhouri made a good point, using __unsafe_unretained on the only reference to the block is dangerous. As long as the block remains on the stack, this will work, but if the block needs to be copied (e.g. it needs to escape to a parent scope), then the __unsafe_unretained will cause it to be deallocated. The following paragraph has been updated with the recommended approach:
What you probably want to do here is use a separate variable marked with __unsafe_unretained that also contains the block, and capture that separate variable. This will prevent it from retaining itself. You could use __weak, but since you know that the block must be alive if it's being called, there's no need to bother with the (very slight) overhead of a weak reference. This will make your code look like
NSArray*(^__block __unsafe_unretained capturedEvaluteAndRecurse)(UIView*);
NSArray*(^evaluateAndRecurse)(UIView*) = ^NSArray*(UIView *view) {
...
[myPassedChildren addObjectsFromArray:capturedEvaluateAndRecurse(subview)];
};
capturedEvaluateAndRecurse = evaluteAndRecurse;
Alternatively, you could capture a pointer to the block, which will have the same effect but allow you to grab the pointer before the block instantiation instead of after. This is a personal preference. It also allows you to omit the __block:
NSArray*(^evaluateAndRecurse)(UIView*);
NSArray*(^*evaluteAndRecursePtr)(UIView*) = &evaluateAndRecurse;
evaluateAndRecurse = ^NSArray*(UIView*) {
...
[myPassedChildren addObjectsFromArray:(*evaluateAndRecursePtr)(subview)];
};
As for needing the __block, that's a separate issue. If you don't have __block, then the block instance will actually capture the previous value of the variable. Remember, when a block is created, any captured variables that aren't marked with __block are actually stored as a const copy of their state at the point where the block is instantiated. And since the block is created before it's assigned to the variable, that means it's capturing the state of the capturedEvaluteAndRecurse variable before the assignment, which is going to be nil (under ARC; otherwise, it would be garbage memory).
In essence, you can think of a given block instance as actually being an instance of a hidden class that has an ivar for each captured variable. So with your code, the compiler would basically treat it as something like:
// Note: this isn't an accurate portrayal of what actually happens
PrivateBlockSubclass *block = ^NSArray*(UIView *view){ ... };
block->stop = stop;
block->evaluteAndRecurse = evaluateAndRecurse;
evaluteAndRecurse = block;
Hopefully this makes it clear why it captures the previous value of evaluateAndRecurse instead of the current value.
I've done something similar, but in a different way to cut down on time allocating new arrays, and haven't had any problems. You could try adapting your method to look something like this:
- (void)addSubviewsOfKindOfClass:(id)classObject toArray:(NSMutableArray *)array {
if ([self isKindOfClass:classObject]) {
[array addObject:self];
}
NSArray *subviews = [self subviews];
for (NSView *view in subviews) {
[view addSubviewsOfKindOfClass:classObject toArray:array];
}
}

ObjC: BAD ACCESS when call blocks ^{} in later functions?

Following this discussion, I've encountered a bad access issue;
A loop has several steps: a, b, c, ... x, y, z:
-(void)cycle:(float)delta{
[self stepA]
[self stepB]
// etc.
[self stepZ]
}
At some point, step x does the following:
// IRQ is an NSMutableArray
// Self is a reference to the engine running the cycles
[IRQ addObject:^{ NSLog(#"hello! %#", self); } ];
Later, step z is to process all "delayed" calls:
for (int i = 0; i < [IRQ count]; i++){
void (^delayedCall)(void) = [IRQ objectAtIndex:i];
delayedCall();
}
[IRQ removeAllObjects];
Result: EXEC_BAD_ACCESS
Now, if step x only adds a plain string with no object reference like follows, step Z works fine:
[IRQ addObject:^{ NSLog(#"hello!"); } ];
Last observation, if a same step both adds blocks to the queue AND iterates over the queue to execute the blocks, then no problem occurs.
Like the reference to an object gets "lost" as the step: method is left?
I don't understand much in this area and will need more help!
edit:
James, just tried the following to avoid that reference cyle:
NSString *userName = #"James";
[IRQ addObject:^{ NSLog(#"hello %#", userName); } ];
and it also happens. How would your solution apply to this?
Thanks in advance!
When you create a block with the ^{} syntax, it's created on the stack. To persist the block for a long period of time (beyond the scope of the function that creates it), you must copy the block into the heap:
void (^ myBlock)(void) = ^ {
// your block code is here.
};
[IRQ addObject:[[myBlock copy] autorelease]];
If using ARC, skip the -autorelease message.
The problem is that block objects are created on the stack. You need to copy blocks to the heap when you expect them to be used after the scope in which they were declared is destroyed, and if the block is not copied for you.
Here you pass an object "down the stack" to a method that is not aware of blocks. Replace
[IRQ addObject:^{ NSLog(#"hello! %#", self); } ];
with
[IRQ addObject:[^{ NSLog(#"hello! %#", self); } copy]];
and the EXC_BAD_ACCESS at this point will go away.
In most cases though, you do not need to copy the block! A couple of examples:
If you return a block from a method ("up the stack"), ARC will automatically copy it.
If you call a method that does not keep the block, the block does not need to be copied, because it stays in scope. Example: the block passed to -[NSArray sortedArrayUsingComparator:].
If you call a method that uses the block later, the method should take the responsible for copying the block, otherwise each and every caller would need to copy the block. All methods/functions from Apple's libraries that I am aware of follow that pattern. Example: the completion block passed to +[UIView animateWithDuration:options:animations:completion:].
It seems the object you pass in.. In your examples: self and userName are being prematurely deallocated. This isn't the behaviour I expect from blocks. As in my previous answer, I expected the problem to be because of too much retention!
As a test, could you try:
NSString *userName = [#"James" retain];
[IRQ addObject:^{ NSLog(#"hello %#", userName); } ];
This would be a memory leak, but it would help indicate if the object is being deallocated.
This is caused by a "retain cycle" where the block is retaining self and self is retaining the block.
Try this:
__block typeof(self) blockSafeSelfReference = self;
[IRQ addObject:^{ NSLog(#"hello! %#", blockSafeSelfReference); } ];
If using ARC, use __unsafe_unretained instead of __block

Block gets released whilst in NSDictionary (ARC)

I'm trying to retain a reference to a Block that's been passed in to my class by a method, to call at a later time. I'm having trouble, however, maintaining a reference to it.
The obvious way, I thought, was to add it to an ivar collection, all of which are supposed to maintain strong references to their contents. But when I try to pull it back out, it's nil.
The code is pretty simple:
typedef void (^DataControllerCallback)(id rslt);
#interface DataController : NSObject {
NSMutableArray* queue;
}
- (void) addBlock:(DataControllerCallback)callback;
- (void) functionToBeCalledLater;
#end
#implementation DataController
- (id) init {
self = [super init];
if (self != nil) {
queue = [NSMutableArray new];
}
return self;
}
- (void) addBlock:(DataControllerCallback)callback {
NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys:
[callback copy], #"callback",
#"some other data", #"data", nil];
[queue addObject:toAdd];
}
- (void) functionToBeCalledLater {
NSDictionary* dict = [queue lastObject];
NSLog(#"%#", [dict objectForKey:#"data"]; //works
DataControllerCallback callback = [dict objectForKey:#"callback"]; //this is nil
callback(#"an arguemnt"); //EXC_BAD_ACCESS
}
What's happening?
Update: I've tried it with [callback copy] and just callback inserting into the dictionary, neither works.
Update 2: If I just stick my block into an NSMutableSet, as long as I call copy, I'm fine. It works great. But if it's in an NSDictionary, it doesn't.
I've actually tested it by putting a breakpoint right after the NSDict is created and the callback never gets inserted. The description reads clearly "1 key-value pair", not two.
I'm currently getting around this with a specialised class that just acts as a container. The callback property is declared as strong; I don't even need to use copy.
The question still stands, though: why is this happening? Why won't an NSDictionary store a Block? Does it have something to do with the fact that I'm targeting iOS 4.3 and thus ARC must be built in as a static library?
Update 3: Ladies and gentleman: I am an idiot.
The code I presented here was obviously a simplified version of the actual code; most particularly, it was leaving some key/value pairs out of the dictionary.
If you're storing a value in an NSDictionary using [NSDictionary dictionaryWithObjectsAndKeys:], you had better be damn sure one of those values isn't nil.
One of them was.
ICYMI, it was causing an early termination of the argument list. I had a userInfo-type argument being passed into one of the "add to queue" methods, and you could, of course, pass in "nil". Then when I constructed the dictionary, chucking in that argument caused the constructor to think I had terminated the argument list. #"callback" was the last value in the dictionary constructor and it was never being stored.
Contrary to popular mis-conception, ARC does not automatically de-stackify Blocks passed as arguments to methods. It only de-stackify's automatically when a block is returned from a method/function.
I.e. this....
[dict setObject: ^{;} forKey: #"boom"];
... will crash if dict survives beyond the scope and you attempt to use the block (actually, it won't in this case because that is a static block, but that is a compiler detail that you can't rely on).
This is documented here:
How do blocks work in ARC?
Blocks “just work” when you pass blocks up the stack in ARC mode, such
as in a return. You don’t have to call Block Copy any more. You
still need to use [^{} copy] when passing “down” the stack into
arrayWithObjects: and other methods that do a retain.
The return value behavior could be automated because it is always correct to return a heap based block (and always an error to return a stack based block). In the case of a block-as-an-argument, it is impossible to automate the behavior in a way that would be both very efficient and always correct.
The analyzer likely should have warned about this use. If it didn't, file a bug.
(I derped a stack when I meant a heap. Sorry about that.)
The compiler doesn't automate blocks-as-parameters for a few reasons:
unnecessarily copying a block to the heap can be a significant performance penalty
multiple-copies of a block can multiply that performance penalty significantly.
I.e.:
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
If that were to imply four Block_copy() operations and aBlock contained a significant quantity of captured state, that'd be a huge potential hit.
• There are only so many hours in the day and automating the handling of parameters is rife with non-obvious edge cases. If this were handled automatically in the future, it could be done without breaking existing code and, thus, maybe it will be done in the future.
I.e. the compiler could generate:
aBlock = [aBlock copy];
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
[aBlock release];
Not only would this fix the problem of a block-as-param, but it would also only produce one copy of the block across all potential uses.
The question still stands, though: why is this happening? Why won't an
NSDictionary store a Block? Does it have something to do with the fact
that I'm targeting iOS 4.3 and thus ARC must be built in as a static
library?
Something bizarre is going on, then. Coincidentally, I've been using blocks-as-values in an ARC based application in the last week and it is working fine.
Do you have a minimal example handy?

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.