After BLOCK Completed only Return

104 views Asked by At

May I know is what are the solution to use in order to make the following code working in order.

- (CGFloat)getRowImageHeight
{
    CGFloat defaultHeight   = 300.f;
    __block CGFloat height  = defaultHeight;

    [self configureImageTVCell:self.itemImageTVCell
                         block:^(UIImage *image, BOOL succeeded) {
                             if( succeeded )
                                 height     = image.size.height;
                         }];

    return height;
}

I would expect the BLOCK to complete first before the return height, it is because I wanted to get the latest height from the completion of BLOCK.

I've tried a few solution by using dispatch_semaphore_t, but it causes deadlock where the simulator actually hang.

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[self configureImageTVCell:self.itemImageTVCell
                         block:^(UIImage *image, BOOL succeeded) {
                             if( succeeded )
                                 height     = image.size.height;

                             dispatch_semaphore_signal(semaphore);
                         }];

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

Running out of idea for this already, need help indeed.

3

There are 3 answers

1
matt On

Basically, what you are trying to do is impossible. On the one hand, you have a method from which you need to return a value immediately:

- (CGFloat)getRowImageHeight
{
    // ... do some stuff ...
    return height;
}

On the other hand, in the middle of that code ("do some stuff"), you are performing an asynchronous operation:

[self configureImageTVCell:self.itemImageTVCell
                     block:^(UIImage *image, BOOL succeeded) {
                         // THIS IS ASYNCHRONOUS
                         if( succeeded )
                             height     = image.size.height;
                     }];

That means, by definition, that the code in the block will run at some future, unknown time. Meanwhile, your outer method getRowImageHeight has finished and returned its value long ago.

That is the nature of asynchronous code execution.

You need to rearchitect your entire approach so that it works in conjunction with your asynchronous code.

Of course, you have not revealed what you are really trying to do, so I can't tell you how to rearchitect it. But let us pretend for a moment that you are trying to populate a table view's cells with images to be downloaded from the Internet. Well, that is a well-established problem with answers all over the place, including many good explanations on Stack Overflow. So you would read those answers.

0
holex On

the good approach would be something like this:

- (void)getRowImageHeightWithCompletionBlock:(void(^)(CGFloat height))completion {
    CGFloat defaultHeight = 300.f;
    [self configureImageTVCell:self.itemImageTVCell block:^(UIImage *image, BOOL succeeded) {
        if(succeeded && completion) {
            completion(image.size.height);
        }
    }];
}
0
Keith Yeoh On

Things that I am trying to do is at view load, I preload the image before it appears on the view. Then get image block to return the image height and I wanted to return back the height to

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

But it seems that answers above told me that the approach is impossible. So I tried to use delegate and return the height to main view and update the cell height by using the code below after the image has been successfully fetch at cell level.

- (void)reloadItemImageTVCellHeight:(CGFloat)height
{
    if( !self.hasUpdatedImageCellHeight ){
        self.hasUpdatedImageCellHeight   = YES;
        self.itemImageCellHeight        = height;
        [self.mTableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:ROW_IMAGE inSection:0]]
                         withRowAnimation:UITableViewRowAnimationNone];
    }
}

In viewDidLoad, I've set self.hasUpdatedImageCellHeight to NO and self.itemImageCellHeight to 300.f. self.hasUpdatedImageCellHeight is to control whether the delegate has already return me any value, if YES then I will update it to YES in order to prevent the delegate codes keep calling again & again.

This is my approach for it, but still it is best if there is an approach that can know the image height natively before the table view being completely loaded.