2013-03-24 22 views
14

Io lavoro su piattaforma iOS, voglio sapere cos'è una funzione delegato e cos'è una funzione di callback? qual è la differenza tra i due tipi di funzione o sono uguali ??funzione delegate vs funzione callback

esempio della funzione di delegato è numberOfRowsInSection in UITableViewDelegate protocollo e l'esempio di funzione di callback è didReceiveLocalNotification in appDelegate.m

Possiamo creare la nostra funzione di callback in Objective-C, se SI, dare un esempio ...

grazie ..

+0

Guarda le risposte fornite su http://stackoverflow.com/questions/12050981/when-we-use-delegate-and-call-back-in-ios –

risposta

67

Un paio di pensieri:

  1. Si suggerisce che didReceiveLocationNotification fosse una "funzione di richiamata", ma in realtà è solo un metodo delegato del protocollo UIApplicationDelegate. Quindi, sia numberOfRowsInSection e didReceiveLocalNotification sono semplicemente metodi delegati.

    Qualcosa più simile ad una funzione di richiamata generica sarebbe il selector quando si pianifica una NSTimer o definire il gestore per un UIGestureRecognizer, dove la scelta di nome del metodo non è predeterminata.

    Oppure le richiamate sono ampiamente utilizzate in CFArray.

  2. Tuttavia, la radice della domanda è meno sulla terminologia, ma piuttosto una questione su come definire un'interfaccia in cui il chiamante può specificare un metodo che alcuni altri invocheranno (in modo asincrono) in una data futura. Ci sono un paio di modelli comuni:

    • Block parameter to method: È sempre più comune per definire metodi che accettano un blocco come parametro. Ad esempio, è possibile avere un metodo che viene definito come segue:

      - (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename completion:(void (^)(NSData *results, NSString *filename))completion { 
          NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
           dispatch_async(dispatch_get_main_queue(), ^{ 
            completion(data, filename); 
           }); 
          }]; 
          [task resume]; 
      
          return task; 
      } 
      

      Questo terzo parametro, completion, è un blocco di codice che verrà chiamato con il download è fatto. Così, si può richiamare il metodo come segue:

      [self downloadAsynchronously:url filename:filename completion:^(NSData *results, NSString *filename) { 
          NSLog(@"Downloaded %d bytes", [results length]); 
          [results writeToFile:filename atomically:YES]; 
      }]; 
      
      NSLog(@"%s done", __FUNCTION__); 
      

      Vedrai che il messaggio "fatto" appare immediatamente, e che completion blocco verrà chiamato quando il download è fatto. Ci vuole un po 'per abituarsi al disordinato pasticcio della punteggiatura che costituisce una variabile di blocco/definizione di parametro, ma una volta che hai familiarità con la sintassi del blocco, apprezzerai davvero questo schema. Elimina la disconnessione tra il richiamo di alcuni metodi e la definizione di alcune funzioni di callback separate.

      Se si vuole semplificare la sintassi di trattare con blocchi come parametri, si può effettivamente definire un typedef per il blocco completo:

      typedef void (^DownloadCompletionBlock)(NSData *results, NSString *filename); 
      

      E poi la dichiarazione di metodo, in sé, è semplificata:

      - (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename completion:(DownloadCompletionBlock)completion { 
          NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
           dispatch_async(dispatch_get_main_queue(), ^{ 
            completion(data, filename); 
           }); 
          }]; 
          [task resume]; 
      
          return task; 
      } 
      
    • Delegate-protocol pattern: L'altra tecnica comune per la comunicazione tra gli oggetti è il modello del protocollo delegato.In primo luogo, si definisce il protocollo (la natura dell'interfaccia "callback"):

      @protocol DownloadDelegate <NSObject> 
      
      - (NSURLSessionTask *)didFinishedDownload:(NSData *)data filename:(NSString *)filename; 
      
      @end 
      

      Poi, si definisce la classe che sarà invoca questo metodo DownloadDelegate:

      @interface Downloader : NSObject 
      
      @property (nonatomic, weak) id<DownloadDelegate> delegate; 
      
      - (instancetype)initWithDelegate:(id<DownloadDelegate>)delegate; 
      - (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename; 
      
      @end 
      
      @implementation Downloader 
      
      - (instancetype)initWithDelegate:(id<DownloadDelegate>)delegate { 
          self = [super init]; 
          if (self) { 
           _delegate = delegate; 
          } 
          return self; 
      } 
      
      - (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename { 
          NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
           dispatch_async(dispatch_get_main_queue(), ^{ 
            [self.delegate didFinishedDownload:data filename:filename]; 
           }); 
          }]; 
          [task resume]; 
      
          return task; 
      } 
      
      @end 
      

      E, infine, l'originale View Controller che utilizza questo nuovo Downloader classe deve essere conforme al protocollo DownloadDelegate:

      @interface ViewController() <DownloadDelegate> 
      
      @end 
      

      e definire il protocollo di me ThOD:

      - (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename { 
          NSLog(@"Downloaded %d bytes", [data length]); 
          [data writeToFile:filename atomically:YES]; 
      } 
      

      ed effettuare il download:

      Downloader *downloader = [[Downloader alloc] initWithDelegate:self]; 
      [downloader downloadAsynchronously:url filename:filename]; 
      NSLog(@"%s done", __FUNCTION__); 
      
    • Selector pattern: Un modello che si vede in alcuni oggetti di cacao (per esempio NSTimer, UIPanGestureRecognizer) è la nozione di passare un selettore come parametro. Per esempio, si può definire il nostro metodo downloader come segue:

      - (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename target:(id)target selector:(SEL)selector { 
          id __weak weakTarget = target; // so that the dispatch_async won't retain the selector 
      
          NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
           dispatch_async(dispatch_get_main_queue(), ^{ 
      #pragma clang diagnostic push 
      #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 
            [weakTarget performSelector:selector 
                withObject:data 
                withObject:filename]; 
      #pragma clang diagnostic pop 
           }); 
          }]; 
          [task resume]; 
      
          return task; 
      } 
      

      Farebbe quindi richiamare che nel modo seguente:

      [self downloadAsynchronously:url 
              filename:filename 
               target:self 
              selector:@selector(didFinishedDownload:filename:)]; 
      

      Ma bisogna anche definire che metodo separato che verrà chiamato quando il download è fatto:

      - (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename { 
          NSLog(@"Downloaded %d bytes", [data length]); 
          [data writeToFile:filename atomically:YES]; 
      } 
      

      Personalmente, trovo questo schema di essere troppo fragile e dipendente da coordinare le interfacce senza alcuna assistenza da parte del compilatore. Ma lo includo per un po 'di riferimenti storici dato che questo pattern è usato un po' nelle classi più datate di Cocoa.

    • Notifications: l'altro meccanismo per fornire i risultati di un metodo asincrono consiste nell'inviare una notifica locale. Ciò è generalmente più utile quando (a) il potenziale destinatario dei risultati della richiesta di rete è sconosciuto al momento in cui la richiesta è stata avviata; oppure (b) potrebbero esserci più classi che vogliono essere informate di questo evento. Pertanto, la richiesta di rete può inviare una notifica di un nome particolare quando viene eseguita e qualsiasi oggetto interessato a essere informato di questo evento può aggiungersi come osservatore per quella notifica locale tramite lo NSNotificationCenter.

      Questo non è un "callback" di per sé, ma rappresenta un altro modello per un oggetto da informare del completamento di alcune attività asincrone.

Questi sono alcuni esempi di modelli di "callback". Chiaramente, l'esempio fornito era arbitrario e banale, ma si spera che dovrebbe darvi un'idea delle vostre alternative. Le due tecniche più comuni, al giorno d'oggi, sono blocchi e pattern delegati. I blocchi sono sempre più preferiti quando è necessaria un'interfaccia semplice ed elegante. Ma per interfacce ricche e complicate, i delegati sono molto comuni.

+3

Ben spiegato ... – Mrunal

+0

@rob post molto utile grazie :) –

+0

@Rob, che dire della scelta di utilizzare pattern o blocchi delegati?Non so se possiamo parlare della "migliore" pratica, ma in quale modo preferisci tra '- (void) didFinishedDownload: (NSData *) data filename: (NSString *) filename' e' - (void) downloadAsynchronously : (NSURL *) nome file url: (NSString *) completamento del nome file: (void (^) (NSData * results, NSString * filename)) completamento'? –