Objective-C: как предоставить блок завершения методу, который использует результат выполнения метода

#ios #objective-c #delegates #objective-c-blocks #completionhandler

#iOS #objective-c #делегаты #objective-c-блоки #обработчик завершения

Вопрос:

Объекту X необходимо загрузить изображение. У него есть URL-адрес изображения (может быть файл сети). У меня есть общий класс imageHandler, который может принимать URL-адрес и получать ImageData. Я хочу, чтобы этот метод мог использовать блок завершения, чтобы настроить, что делать с загруженным изображением.

Я знаком с тем, как это сделать, используя шаблон делегирования. Например

 @protocol ImageHandler: NSObject
-(void) getImageFromURL:(NSURL *)url forDelegate:(id <ImageRequestor>)delegate;
@end

@protocol ImageRequestor: NSObject
-(void) image:(UIImage *) image RetrievedForURL:(NSURL *)url withError:(NSError *)error;
@end
  

Итак, в основном класс ObjectX getImageFromURL: метод делегирования с делегатом как self . Он соответствует протоколу ImageRequestor.

Объект ImageHandler хранит URL-адрес для делегирования сопоставления в хеш-таблице. После того, как это будет сделано, вызывает image:RetrievedForURL:withError: для делегата. И в этом методе я действую на изображении и ошибке.

Как мне добиться того же эффекта, используя блоки завершения, где я передаю «что я хочу сделать с полученным изображением» в виде фрагмента кода.

Один из подходов, который я вижу, выглядит следующим образом. Но, похоже, для этого требуется, чтобы реализация метода ImageHandler вызывала завершение с определенными аргументами. Является ли это общепринятым способом реализации этого (т.Е. Обработчик завершения полагается на метод, получающий его, чтобы вызвать его с правильными аргументами)?

 @protocol ImageHandler: NSObject
-(void) getImageFromURL:(NSURL *)url withCompletionHandler:(void (^)(UIImage *image, NSError * err))completionBlock;
@end
  

Реализация getImageFromURL:(NSURL *)url withCompletionHandler:(void (^)(UIImage *image, NSError * err))completionBlock выглядит следующим образом

 getImageFromURL:(NSURL *)url withCompletionHandler:(void (^)(UIImage *image, NSError * err))completionBlock
{
 //Get UIImage from URL, store it UIIMage ObjectX
 //if error happens, store it in NSError objectY, else it is nil
 //call completion handler 
 completionBlock(<UIIMage objectX>,<NSError objectY>);
}
  

Ответ №1:

Да, то, что вы показали, является типичным способом сделать это. Посмотрите на собственные API Apple, чтобы увидеть, что они делают это таким же образом. Например, -[NSURLSession dataTaskWithURL:completionHandler:] , объявление которого:

 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url 
                        completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
  

Ответ №2:

Напишите следующее в вашем ImageRequestor.h файл:

 #import <Foundation/Foundation.h>

// Here you define your custom response block with the desired parameters
// That in this case are UIImage and NSError
typedef void (^ResponseBlock)(UIImage *image, NSError *error);

@interface ImageRequestor : NSObject

// Here you define the method that use your custom response block
- (void)getImageFromURL:(NSURL *)url withCompletionHandler:(ResponseBlock)completionBlock;

@end
  

Почти готово, теперь откройте файл ImageRequestor.m и добавьте следующее:

 #import "ImageRequestor.h"

@implementation ImageRequestor

- (void)getImageFromURL:(NSURL *)url withCompletionHandler:(ResponseBlock)completionBlock {
  // Do all your stuff to get the image and the error
  // And set them into your completion block
  // if there is no error that should be nil

  // completionBlock(YourFetchedImage, YourFetchedError);
  // completionBlock(nil, YourFetchedError);
  completionBlock(YourFetchedImage, nil);
}

@end
  

И, наконец, вы можете назвать это чем-то вроде этого:

 ImageRequestor *imageRequestor = [[ImageRequestor alloc] init];
[imageRequestor getImageFromURL:yourURL withCompletionHandler:^(UIImage *image, NSError *error) {
  // Your implementation in here
}];
  

Такого рода материалы выглядят намного лучше в одноэлементном классе :]

Надеюсь, это поможет, Удачи!!