11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swiftクラスのプロパティの初期化処理をどうにかしたい

Last updated at Posted at 2015-11-28
Xcode7.1.1
  ~  swift --version
Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76)
Target: x86_64-apple-darwin15.0.0

##動機
プロパティ名が無駄に長い場合、入力補完があるとはいえ、いちいちself.longNameProperty.hoge = fugaと書くのが面倒臭い。
短いプロパティ名をつけても、プロパティのプロパティが短い名前とは限らない。

self.longNameProperty = UIView(frame: CGRectMake(0,0,100,100))
self.longNameProperty.backgroundColor = UIColor.whiteColor()
self.longNameProperty.autoresizesSubviews = false
self.longNameProperty.layer.cornerRadius = 8
self.longNameProperty.layer.shadowColor = UIColor.blackColor().CGColor
self.longNameProperty.layer.borderColor = UIColor.redColor().CGColor
self.longNameProperty.layer.borderWidth = 5

短い名前の変数を作って、最後に代入することもできるが、複数のプロパティを設定しないといけない場合、同じスコープに短い似たような名前の変数がたくさんできてしまうのが嫌。

let v1 = UIView(frame: CGRectMake(0,0,100,100))
v1.backgroundColor = UIColor.whiteColor()
v1.autoresizesSubviews = false
v1.layer.cornerRadius = 8
v1.layer.shadowColor = UIColor.blackColor().CGColor
v1.layer.borderColor = UIColor.redColor().CGColor
v1.layer.borderWidth = 5
self.longNameProperty = v1
let v2 //...
let v3 //...
//同じスコープにv1, v2, ..みたいなのがたくさん出るのが嫌。

また、同じような初期化をするなら、関数化すれば良いだけだが、各プロパティで全く異なる初期化処理をする場合は関数化する意味がない。

##解決案
###実装方法(ジェネリクス版)
NSObjectを拡張してスコープを作るメソッドを生やしてみる。

NSObject+Ext
extension NSObject {
    func scope<T: NSObject>(block: (T)->Void) -> Self {
        if let obj = self as? T {
            block(obj)
        }
        return self
    }
}

###使用方法
長いプロパティ名を書く数が減り、短い変数名の氾濫を抑えられる。
インデントがついて読みやすい。

self.longNameProperty = UIView().scope { (v: UIView) in
    v.frame = CGRectMake(0, 0, 100, 100)
    v.backgroundColor = UIColor.whiteColor()
    v.autoresizesSubviews = false
    v.layer.scope { (l: CALayer) in
        l.cornerRadius = 8
        l.shadowColor = UIColor.blackColor().CGColor
        l.borderColor = UIColor.redColor().CGColor
        l.borderWidth = 5
    }
}

###問題点等

  • パフォーマンスは考えてない
  • インデントなしの方が見やすい?

私はインデントがついている方が読みやすいと思いますが、一番初めの例のように左詰めで揃っている方が読みやすいという人もいるでしょう。 万人ウケするとは思ってません。

  • クロージャの引数の型を指定しなければならない

本当は次のようにしたかったのですが、ジェネリクスにSelfは使えないし...
良い方法があれば教えてください。->プロトコルエクステンションを使う。

self.longNameProperty = UIView().scope { v in
//...
}
  • NSObjectでしか使えない

AnyObjectAnyは拡張できないので、NSObjectを継承していないクラスや構造体には使用できない。

##プロトコルエクステンションを使う方法(追記)
tom-uさんに教えていただいた方法。
プロトコルだとSelfを引数の型にできるので、プロトコルエクステンションでscopeメソッドを作る。

protocol  ScopeProtocol {}
extension ScopeProtocol {
    func scope(block: (Self) -> Void) -> Self {
         block(self)
         return self
    }
}
extension NSObject: ScopeProtocol {}

これで、型を指定せずに次のように書けます。

self.longNameProperty = UIView().scope { v in
    v.frame = CGRectMake(0, 0, 100, 100)
    //...
}

##doブロックを使う方法 (追記)
コメント欄でkishikawakatsumiさんに教えていただきました。
doとcatchがセットだと思っていましたが、doブロックは単体で使えます。
こっちの方が良いかもしれません。

do {
    let v = UIView(frame: CGRectMake(0, 0, 100, 100))
    v.backgroundColor = UIColor.whiteColor()
    v.autoresizesSubviews = false
    do {
        let l = v.layer
        l.cornerRadius = 8
        l.shadowColor = UIColor.blackColor().CGColor
        l.borderColor = UIColor.redColor().CGColor
        l.borderWidth = 5
    }
    self.longNameProperty = v
}

##closureのみを使う方法(追記)
su_kさんに教えていただいた方法。
closureを作って直ちに使う。protocol extensionとか書かなくてもいい。

self.longPropertyName = {
    let v = UIView(frame: CGRectMake(0, 0, 100, 100))
    v.backgroundColor = UIColor.whiteColor()
    v.autoresizesSubviews = false
    _ = { // _ = がない場合はdoを使えと言ってくる
        let l = v.layer
        l.cornerRadius = 8
        l.shadowColor = UIColor.blackColor().CGColor
        l.borderColor = UIColor.redColor().CGColor
        l.borderWidth = 5
        }()
    return v
    }()

layerのところは素直にdoで良いでしょう。
control+iした時の"}"の位置がscope(){}の場合と違いますね。

##各方法の特徴(追記)
最終的にはパフォーマンスとXcodeのインデンテーション(範囲指定してctrl+i)した時の読みやすさと手間でどれを使うか決めれば良いと思います。読みやすさは個人差があると思いますが...

  • scope():

拡張が必要な分、少しだけ手間。propertyが既に別な場所で作られて設定だけをしたい場合にも同じような形式で記述できるのがいい。見やすい?

  • do:

拡張必要なし。ベンチマークしたわけではないですが、パフォーマンスが良いはず。

  • closureのみ:

拡張必要なし。doより見やすい?

11
11
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?