一畳のくつろぎタイム

このブログでは紹介する商品画像をAmazonアソシエイトより借りています。画像やリンクにはアフィリエイト広告が含まれる事があります

2018年9月5日水曜日

SwiftのOptionalがうざいところ

Swiftのオプショナルがうざい

なにがうざいかと言うと、なかったら作る処理が簡潔に書けない。

コードオンリーでUITableViewを作っていると、UITableViewCellを作るときにdequeueReusableCellメソッドがnilを返した場合に新しくインスタンスを作り、nilでなければメソッドより返された値を使う。
??演算子を使用すれば、それに近いことはできる。だがしかし、新たに作成したUITableViewCellの初期設定がしたいのだ。??の後ろが1行じゃ足りなくて、もう少し行が欲しい。
        let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "hoge") ?? UITableViewCell(style: .default, reuseIdentifier: "hoge")
Objective-Cの場合、至ってシンプルである。nilだったら作って設定すればいい。
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"hoge"];
    
    // 再利用セルがない場合は
    if( cell == nil ) {
        // 作成する
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"hoge"];
        // ここで作ったcellの共通設定
    }
    
    // ここで内容の更新処理
     
    return cell;
}



Swiftの場合、guard文だと処理の中断が必須なため。guard文の中と外で2回内容の更新処理を書かなくてはならなくなる
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "hoge") else {

            let cell = UITableViewCell(style: .default, reuseIdentifier: "hoge")
            
            // 初期設定

            // 内容の更新処理
            
            return cell
        }


        // 内容の更新処理
        

       return cell
    }

オプショナルバインディングだと大した処理がないのに、代入のためだけのif-else両方が必要になる
        let cell:UITableViewCell
        
        if let unwrappedCell = tableView.dequeueReusableCell(withIdentifier: "hoge") {
            cell = unwrappedCell
        } else {
            cell = UITableViewCell(style: .default, reuseIdentifier: "hoge")
            // 初期設定
        }

        // 内容の更新処理

        return cell
外のスコープに変数を用意するぐらいだったら、オプショナルバインディングなどいらぬ。
以下のようにオプショナル構文を使わないで書いたって同じことだ。
        // セルの再利用可能か取得して見る
        let optCell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "hoge")
        
        let cell:UITableViewCell
        
        if optCell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "hoge")
            
            // 初期設定をする
            
        } else {
            // アンラップする
            cell = optCell!
        }
        
        // 内容の更新処理をする
        
        return cell
わずかではあるが、変数を2個用意しないで済むオプショナルバインディングを使った方が簡潔なのがなんか腹立たしい。

内容の更新処理をメソッド化すれば2回同じことは書かないで済むし、UITableViewCellのサブクラスを作れば??演算子でいけますが、だがObjective-Cと同じことができないのがなんかうざい。
??演算子の後ろにすぐ実行するUITableViewCellを戻り値とするクロージャ書いたらなんかできそうな気がするも、邪道な感じがして試したくもない。

とりあえず外のスコープの変数用意しておいて、オプショナルバインディグが正攻法かなと思う。