1

I'm using Blocks instead of delegates for callbacks between viewControllers but i'm not able to figure out why this scenario is not working:

So I have a mainViewController which is calling a detailViewController, when returning the tableView which is a property on the mainViewController needs to be reloaded.

DetailViewController *actionDetail = [[DetailViewController alloc]initWithSaveBlock:^{
        [self.tableView reloadData]  //app crashes here
}];

in the detailViewController 'save' is called when the user taps the 'Save button'

- (void)save
{
    if (self.saveBlock)
        self.saveBlock();

    [self dismissModalViewControllerAnimated:YES];
}

for some reason the [self.tableView reloadData] does not go along with [self dismissModalViewControllerAnimated:YES]. If I remove one of the two the app seems to work fine, obviously than I'm missing the intended behavior.

When the app crashes the message in the console is:

Previous frame inner to this frame (gdb could not unwind past this frame)

Anyone an idea why this is not working ?

Update: SaveBlock is defined like this

typedef void (^SaveBlock)();

@interface DetailViewController : UIViewController
{

}
@property(nonatomic,assign)SaveBlock saveBlock;
mattjgalloway
  • 34,792
  • 12
  • 100
  • 110
Oysio
  • 3,486
  • 6
  • 45
  • 54

2 Answers2

7

You need to declare your block property as copy:

@property(nonatomic, copy) SaveBlock saveBlock;

You can't use assign or strong. The reason being that you need to ensure that you've got your own copy of the block. The one passed in might be on a stack frame which might be gone by the time you come to run the block. So you need to trigger a Block_copy() to ensure that the block gets copied to the heap (or just retained if it's already there).

[Note: that explanation is very terse. I suggest you go read about the block runtime if you want to understand it fully.]

mattjgalloway
  • 34,792
  • 12
  • 100
  • 110
  • I have one additional question, do I need to release the block (how?) when using copy ? – Oysio May 22 '12 at 21:36
-1

Applies under ARC ONLY

You need to declare your block property as strong, declaring it as copy is not required. So the declaration should be:

@property (nonatomic, strong) SaveBlock saveBlock;

Why not copy?

When passed to your initWithSaveBlock: the block may be stack allocated, or it may not be. In either case you need to retain it. If the block is on the stack then retaining it will cause it to be copied onto the heap. If the block is already on the heap then it need not be copied. So using copy will work but may include a redundant copy, using strong avoids that.

See also this answer for an explanation.

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
  • 3
    Where is it documented that `retain` will `copy` to the heap if not already on the heap? – Paul.s May 20 '12 at 20:50
  • Perhaps this is true in ARC (I don't know), but retain will not move it to the heap in a non-ARC environment. – Joel May 20 '12 at 23:00
  • @Joel - Oops, yes, thanks for pointing that out. ARC knows to copy, MRC retain doesn't. Added a rider to the answer. – CRD May 21 '12 at 04:04
  • 1
    @CRD Block_copy is optimised to just retain if it's already on the heap so there is no problem using copy. – mattjgalloway May 21 '12 at 10:06
  • @Paul.s - see – CRD May 21 '12 at 21:54
  • @mattjgalloway - I wasn't sure about that, hence the "may include". The advantage in using `strong` it saves folks needing to know about stack vs. heap blocks when using ARC; the difference just becomes an implementation optimisation rather than something the user has to manage. – CRD May 21 '12 at 21:58
  • 1
    But you don't need to know about stack and heap blocks if using copy. Also, it's defined semantics of copy that it does the right thing with copying to heap or just retaining if already on the heap. Copy is the right thing to use. – mattjgalloway May 22 '12 at 07:22
  • @mattjgalloway - I think this is down to preference :-) I doubt you declare `@property (copy) NSNumber *temperature;` even though its valid, but you do declare `@property (copy) NSString *destination;` - the latter may be mutable and you want a copy. Blocks are not mutable and ARC allows the implementation detail of how they are stored to be just that. So I'd pick `strong` *by personal preference*. Sure there is "no problem using copy" as you say, you just have to explain why you're copying an immutable object, which brings in that implementation detail unnecessarily *in my opinion*. OK :-) – CRD May 22 '12 at 20:18
  • 1
    I'd say strong is wrong though. A block by its nature has to be copied when you want to use it later. Also it's only since the latest clang that strong does the right thing (which is exactly the same as copy) and this is not documented anywhere I can find, so it might actually jut be a consequence. If you can find the documentation where it says strong now does this then I'd be happy to say we can now use strong. Otherwise copy is the only one that uses the correct semantics in this case. – mattjgalloway May 22 '12 at 21:19
  • @mattjgalloway - No, "A block by *implementation detail* has to be copied...", there is nothing inherent in block semantics which requires stack allocation in the first place, its an *implementation optimisation*. Pre-ARC this is an implementation detail which programmers had to unfortunately deal with, post-ARC they do not. What you're looking for is probably "... whenever these semantics call for retaining a value of block-pointer type, it has the effect of a Block_copy..." - see link in earlier comment. – CRD May 22 '12 at 23:20
  • I'm slightly unsure about the wording in that. And *"The optimizer may remove such copies when it sees that the result is used only as an argument to a call."* seems to me to imply that if the block is just passed as an argument (i.e. when setting the property) then the optimiser is allowed to remove the copy. Also this in the docs is quite damning - *"There is a builtin class that all block objects are considered to be objects of; this class implements retain by adjusting the reference count, not by calling Block_copy."*. – mattjgalloway May 22 '12 at 23:56
  • But anyway, I'll add it to my list of things to geek out with the Apple engineers at WWDC 2012 about :-P. – mattjgalloway May 22 '12 at 23:56
  • @mattjgalloway - Those extra words are again an optimisation, you don't need to be concerned - redundant copies may be elided. Once you have ARC your concern is maintaining references to the objects you need; ARC handles the retain etc. needed for this, and in the case of blocks, moving things from stack to heap if that is needed. However ARC's support for the later is type-based, don't go passing blocks as `id` or bad things may happen. – CRD May 23 '12 at 00:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/11606/discussion-between-mattjgalloway-and-crd) – mattjgalloway May 23 '12 at 00:10
  • ARC certainly is not handling anything here. Why would it? If anything it's just a change in the semantics of a block property defined as strong from before. How is it anything to do with ARC? I am guessing they changed it so that you can directly set an ivar safely and has the side effect of making a strong property work as it'll be changing objc_storeStrong for block pointer types. – mattjgalloway May 23 '12 at 00:11
  • @mattjgalloway - the changes need not have been tied to ARC, indeed Block_copy could have been automatic from the start, the connection is just historical. For the user ARC & hiding the implementation optimisations of blocks coincide with the former getting the headline. – CRD May 23 '12 at 01:13
  • FYI, I asked about this and `strong` is fine now. It was a bug before that it didn't `Block_copy`. Interestingly, `retain` is not fine. i.e. `strong` != `retain` in this case. – mattjgalloway Jun 19 '12 at 08:43