2016-08-29 2 views
0

最近、関数がWebリクエストから必要な情報を取得するとUIを更新する必要があるようなことに取り組んできました。非同期呼び出しの空のクロージャ

この関数で空のクロージャを渡して同じ関数でクロージャを呼び出すと、データがダウンロードされた後にUIが更新されることがわかりました(クロージャなしでUIを更新しようとする前にデータがまだダウンロードされていたためにプログラムがクラッシュしました)。私のビューコントローラに続いて

func DLDetails(completed: DLComplete) { 

let url = "string" 
Alamofire.request(GET, url).responseJSON { response in 

    //Getting all the data I need and putting them in variables 

} completed() 

:ここ

typealias DLComplete =() ->() 

は、関数がどのように見えるかです:したがって、基本的

ViewDidLoad() { 
    super.viewdidload() 
    DLDetails() { 
     //call function that updates UI 
    } 
} 

、イム

は、最初に私はタイプのエイリアスを作成しました不思議なことに、なぜこのような空のクロージャを作成すると、プログラムは最初にデータをダウンロードでき、データがダウンロードした後、UIを更新します。すべてはどのように機能していますか?

私のDLDetails関数で空のクロージャを呼び出す方法は私のVCでこの関数を呼び出すことができるので、私は更新UI関数を呼び出すことができる別のクロージャを開きますか?

私は閉鎖に新しいので、データがダウンロードされた後、() ->()が私のView Controllerで更新UI機能を呼び出すことができる方法さえもわかりません。

+2

'() - >()'は空のクロージャではありません。パラメータを持たず、戻り値もないのは、クロージャの*型*です。 –

+0

...メインスレッドのUIを更新します。 –

答えて

2

あなたは言う:

私は() ->()が何をしているか、または何をしているか(String?, NSError?) ->()理解する問題を抱えています。

これらの構築物自体は、何もしていません。 1つのメソッドから別のメソッドに渡すことができるコードの一部であるclosureを定義するだけです。この場合、考え方はviewDidLoadが「ある非同期ネットワーク要求を開始し、すぐに戻り、アプリケーションがメインスレッドをブロックしないようにすることができます(つまり、ユーザーインターフェイスはフリーズしません)」ということです。完了したら呼び出すことができるので、非同期リクエストが完了したらUIを更新できます」

したがって、() ->()は、この変数がクロージャを保持すると言っています。 () ->()では、viewDidLoadによって提供されるクロージャは、パラメータを取らず値を返さないクロージャと定義されています。 (String?, NSError?) ->()では、クロージャに2つのパラメータ、オプションの文字列とエラー参照が渡されますが、値は返されません。要するに、ダウンロードメソッドに、要求が成功した場合は文字列値を返す機会を与え、要求が失敗した場合はエラーオブジェクトを返す機会を与えています。したがって、viewDidLoadはクロージャを提供し、ダウンロードメソッドは非同期要求が完了したときにクロージャを呼び出す責任があります。私は、なぜプログラムが最初のデータをダウンロードすることができ、このような空の閉鎖を作成しない、思ったんだけど

、およびデータがダウンロードされると、その後、UIを更新します。

あなたが尋ねます。すべてはどのように機能していますか?

すべてのタイミングは非同期メソッドに関係します。 AlamofireのresponseJSONメソッドは直ちに戻りますが、その後続のクロージャは非同期に(つまり要求が完了した後に)呼び出されます。したがって、ビューコントローラでUI更新をトリガする場合は、DLDetailsメソッドで独自の補完ハンドラパターンを採用し、responseJSON完了ハンドラが呼び出されたときにのみ補完ハンドラを呼び出します。


ところで、あなたのAlamofireの例では、それはあなたのコードに示すようにしないの後、completed()内部responseJSON閉鎖を置くようにしてください。このアイデアは、要求が完了したときにクロージャを呼び出すことです。クロージャをクロージャー内に入れないと、要求が完了する前に途中で呼び出されます。

DLDetailsの中で直接モデルを更新するのではなく、取り出されたデータを返すようにcompletedを定義することをお勧めします。たとえば、文字列を返す場合は、は(String?) ->()となります(たとえば、リクエストが成功した場合はStringを渡し、そうでない場合はnilを返します)。また、ErrorTypeまたはNSErrorの参照を返すこともできます。そのため、エラーが発生した場合、View Controllerは特定のタイプのエラーに適切なUIを提示する機会があります(たとえば、認証エラーにより再認証フロー、ネットワーク接続エラーにより異なるUIが発生する可能性があります)。

typealias DownloadCompletion = (String?, NSError?) ->() 

func downloadDetails(completionHandler: DownloadCompletion) { 
    let url = "string" 
    Alamofire.request(.GET, url) 
     .validate() 
     .responseJSON { response in 
      switch response.result { 
      case .Failure(let error): 
       // handle errors (including `validate` errors) here 
       print(error) 
       completionHandler(nil, error) 
      case .Success(let value): 
       // handle success here, retrieving/parsing the necessary value(s) 
       let string = ... 
       completionHandler(string, nil) 
      } 
     } 
} 

そして、あなたのビューコントローラで:

override func viewDidLoad() { 
    super.viewDidLoad() 
    downloadDetails() { responseString, error in 
     guard let string = responseString else { 
      // handle failure here 
      return 
     } 

     // do something with `string` here, e.g update model and/or UI 
    } 

    // But don't try to use `responseString` or `error` here, because the above 
    // closure runs asynchronously (i.e. later), and will not have been called by 
    // the time we get here. 
} 

明らかに、downloadDetailsでの解析は、単純なStringを解析するよりも複雑である可能性が高い、これだけの最初のパラメータを変更しますあなたのクロージャは、あなたのケースで適切なデータであればどんなものでもかまいません。

+0

ありがとうございましたが、 "() - >()"や何 "(String?、NSError?) - >()"が何をしているのか理解できません。 – JasonP

+0

数時間情報を消化した後、私は今理解していると思います!ありがとう! – JasonP

関連する問題