くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

エミュレーターでネットに接続する

今回は小ネタです。

以前、エミュレーターでインターネットに接続できずなぜ?と思ったことがあります。

デフォルトでAndroid WifiというWifiに接奥されていますが、設定をしないとインターネットに接続できないようです。 早速みていきましょう!

まずはエミュレーターがあるパスに移動しましょう。

preferenceからAndroid SDKのパスを確認しましょう。

f:id:kumaskun:20201111233451p:plain

ほとんどの場合、下記の場所に置いてあるはずです。

cd /Users/xxx/Library/Android/sdk/emulator

次に使用できるエミュレーターを確認します。

./emulator -list-avds

すると、下記のように一覧が表示されます。

Nexus_5X_API_29
Pixel_2_XL_API_29

そして、下記コマンドで起動するとインターネットに接続できます。 Nexus_5X_API_29の箇所は一覧に表示された名前で変更すると変更したエミュレーターで起動できます。

./emulator -avd Nexus_5X_API_29 -dns-server 8.8.8.8

小ネタですが、1時間くらいハマったので忘れないようにしなきゃと思いました。

Contextについて

今回はAndroidでよく出てくるContextについて説明しようかと思います。

ContextはAndroidでよく出てくると思うのですが、漠然としたものでフワフワしていたので、少し調べてみました。

Contextとは?

Contextを公式で確認すると下記のような記載があります。

Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

Android OS の環境やリソースへのアクセスの窓口となるコンポーネントです。 データベースやファイル、アプリで利用する画像データなどへのアクセスの窓口となるほか、他のアプリとの連携や、アプリ情報へのアクセスの窓口ともなります。 よく使うのはリソース取得、Activityの起動、intentの送受信などかなあと思います。 また、Context はライフサイクルを持っていて、 端末の様々な 状態遷移の管理も、Contextが行います。

Contextはいくつかの種類が存在します。アプリを作る際には、 どの種類のContext かを意識する必要があります。

Contextの種類

  • Application:Android アプリ全体を統括します。 アプリケーションが起動してから終了するまでのライフサイクルを管理します。シングルトンなので、1つしかないです。
  • Activity:1つの画面を統括します。MVC モデルの、コントローラに相当する役割を担います。 1つの画面が構成され、バックグラウンドに移るか、終了するまでのライフサイクルを管理します。
  • Service:画面を持たず、バックグラウンドで動作します。 バックグラウンドで常に動き続ける常駐型の Service と、都度起動と終了をする Service の2種類があります。 Service が作られてから終了するまでのライフサイクルを管理します。

Contextの取得

Contextを取得することがよくあると思います。大まかには下記のようにして取得します。

  1. Activityのthis
  2. ActivityやApplicationのgetApplicationContext
  3. ViewやFragmentのgetContext

1は特になじみのある方が多いと思います。1はthis@MainActivityのようにすることもできます。

this@にするパターンは下記の例の場合です。

class A {
    inner class B {
        fun Int.foo() {
            val a = this@A
            val b = this@B

            val c = this
            val c1 = this@foo
        }
    }
}

上記の場合はthisはInt foo()のレシーバーであるIntになります。

なので、スコープ外にあるAやBを取得する場合にはthisをつけます。

ここで注目したいのはActivityでContextを取得する場合はthisgetApplicationContextの2つの方法があるということです。

どちらを使っても良さそうで、しかもgetApplicationContextはnullにならないので、ActivityでgetApplicationContextを使おうと思う方もいるかもしれません。

ただ、その場合は意図しない挙動になったり、下手したらメモリリークを引き起こす可能性もあるということを頭に入れておかないといけません。

まず、getApplicationContextの場合はAndroidManifest.xmlに設定したテーマが参照され、thisの場合はAndroidManifest.xmlのに設定したテーマが参照されるかという違いがあります。

そして、常にApplicationContextの方を参照してしまうと意図しないView(標準ダイアログなど)に割り当てられたり、メモリリークの原因となる可能性があります。

QA Stackというサイトに下記のパターン別取得方法が載っていたので、参考になるかもしれません。

f:id:kumaskun:20201108195620p:plain
パターン別Context取得推奨方法

この表をみると基本的にはライフサイクルに従うものはActivityで取得すべきなので、thisを使い、AlpplicationにまたがるものはgetApplicationContextがいい気がします。

例えば、AlertDialogは専用のクラスを作る場合が多いかもしれませんが、表示させるのはActivityでの画面の上なはずです。 そうすると、ライフサイクルに従った方が合理的な印象はあります。

ただ、この辺りは諸説いろいろあるため、いろんな意見を聞いてみたいです!!

自分はこうしているなどあればコメントしてくださると嬉しいです!!

参照

Kotlin リファレンス

getContext()、getApplicationContext()、getBaseContext()と“ this”の違い

Android:引数はthisか?getApplicationContextか?ActivityとApplicationの違い

Xcodeのショートカット集

今回はXcodeのショートカットをまとめようかと思います。

なぜ、この記事を書こうかと思ったのかというと、ショートカットを使うことで開発が効率的に進められることを体感したからです。

最近、ペアプロを体験して、相手の開発をみる機会がありました。 そこで、ショートカットを使うことでこんなにも差があるのかと感じたので、使おうと思いました。

ただ、Xcodeのショートカットをあまり知りませんでした。 むしろ、そんなにショートカットは充実していないと思い込んでいましたが、調べてみるとそれなりに充実していたので、参考用にまとめようと思います。

ビルド・実行のショートカット

結構無意識で使っているコマンドが多いかもしれないですが、載せます。

操作 コマンド
ビルド Cmd + B
実行 Cmd + R
クリーンビルド Cmd + Shift +K
起動しているアプリの終了 Cmd + .
一時停止・再開 Cmd + Control + Y
ブレークポイントの追加・削除 Cmd + \
ステップオーバー F6
ステップイン F7
ステップアウト F8

検索

これは他のIDEでもありますし、よく使う方も多いと思います。

操作 コマンド
ファイル内検索 Cmd + F
フォルダ内検索 Cmd + Shift + O
プロジェクト全体の検索 Cmd + shift + F
ファイル内のメソッド一覧表示と検索 Control + 6

ウィンドウ操作

個人的にはこの辺りはあまり知らなくて、知ってたら1番効率化できそうだと思っている箇所です。

操作 コマンド
ナビゲーターエリア(左側のファイル一覧とか)の表示・非表示 Cmd + 0
ユーティリティエリア(右側の制約を設定したりする箇所)の表示・非表示 Cmd + Option + 0
デバッグエリア(コンソールログとか)の表示・非表示 Cmd + Shift + Y
タブの追加 Cmd + T
左上ナビゲーターエリアのメニューの移動 Cmd + 1 (2,3,4としていくと次のメニューに移動する)

コード編集

実装大好きな人は覚えておいて損はないはず!!

操作 コマンド
選択中のクラス・メソッド・変数の定義へ移動 Cmd + Control + J
自動インデント Control + I
編集を一つ進める Cmd + Shift + Z
インデントを1つ下げる Cmd + [
インデントを1つ上げる Cmd + ]
カーソルを行頭へ移動 Cmd + ⬅ or Control + A
カーソルを行末へ移動 Cmd + ➡ or Control + E
カーソルをファイルの先頭に移動 Cmd + ⬆
カーソルをファイルの最後に移動 Cmd + ⬇
右に一単語 移動 Option + ➡
左に一単語 移動 Option + ⬅
ソースを折りたたむ Cmd + Option + ⬅
折りたたんでいるソースコードを展開 Cmd + Option + ➡
カーソルを行頭へ移動 Cmd + ⬅ or Control + A
カーソルを行末へ移動 Cmd + ➡ or Control + E
現在のカーソル位置から行末をまとめて選択 Cmd + Shift + ➡
現在のカーソル位置から行頭をまとめて選択 Cmd + Shift + ⬅
フォント拡大 Cmd + 「+」
フォント縮小 Cmd + 「-」
選択した変数を一括変更 Cmd + Control + E
クイックリファレンス表示 Option + クリック

シュミレーター

シュミレーターをよく使う方は覚えた方がいいかもしれません。

操作 コマンド
左回りに端末を回転 Cmd + ⬅
右回りに端末を回転 Cmd + ➡
ホームボタンクリック Cmd + H
ロック Cmd + L
キーボードの表示・非表示 Cmd + K
Macキーボードへの切り替え Cmd + shift + K
スローアニメーションモードのオンオフ Cmd + T
マルチタスクへ移動 Cmd + H を2回連続
スクリーンショットの取得 Cmd + S
端末を振る Cmd + control + Z

ここまでいろんなショートカットを記載しました。結構な数ありますが、使いこなすと多くの時間を簡略化できる気がします。 ぜひ、使ってみて効率的な開発ライフをお過ごしください!

追記があれば、追記していきます!!

Optional型について

Swiftだけではないですが、SwiftにはOptional型という特徴的な型があります。

今回はそんなOptional型についてお話しできたらと思います!

Optional型とは

Optional型とは通常の値とは別にnullをとれる型です。 他の言語ではString型やInt型といった通常の型にnullという値が入ると思いますが、Swiftでは通常の型にはNULLを入れることができず、もしnullを扱いたい場合はOptional型を使わないといけません。

SwiftのOptional型はnullの場合に文法的にエラーを返してくれるので、文法通りに書けばバグをある程度回避できるメリットがあります。

Optional型を使おう

では実際にどのようにOptional型を使うかというところを見ていきます。

まず、Optional型はを使います。

宣言時には下記のようにします。

var a: Int?

このように、Intの後ろにをつけるとInt型ではなく、Optional型になります。

また、Int型は初期化しないとコンパイルエラーになりますが、Optional型ではコンパイルエラーにはなりません

let a: Int?  //宣言時に初期値を定義しなければ自動的にnilが入る

var b: Int? = 2 //nilじゃない値も入れることが出来る
b = nil //nilを代入することも出来る

var c: String //nilを入れれないのでエラー

はnullが入る可能性があるという意味なので、Optional型ではnullが許容されています。

ちなみに、Optional型の値を見てみると、下記のようになります。

let a: Int = 1
print(a) // 1

let b: Int? = 2
print(b) // Optional(2)

上記の場合だと、Optional型はInt型ではないので、下記のように計算はできません。

let a: Int = 1
let b: Int? = 2

let c = a + b //コンパイルエラー

では、計算したい場合はどうすればいいでしょうか? そのときに使うのが!です。

let a: Int? = 1
print(a) // Optional(1)
print(a!) // 1

!を使うことをアンラップと言います。このアンラップをすることで、nullが入らないことが前提と定義できるので、上記の場合はOptional型ではなくInt型として使うことができます。

しかし、アンラップしている値にnullが入ったらクラッシュするため注意が必要になります。

let a: Int? = nil
let b = a!  //クラッシュ

クラッシュしないためにnullチェックが必要になりますが、Swiftではif-let構文を使うことが多いです。

let a: Int? = nil
if b = a {
    let d = b!
    print(d) // nullではない場合に通る
} else {
    print("nil") //aがnullの場合の処理
}

上記のようにaがnullの場合はelseの方を通るのでnullの場合の処理を記載することができるので、nullチェックを行うことができます。

また、下記のように変数を使わない場合は_(アンダースコアー)で記載することも可能です。

let a: Int? = nil
if _ = a {
    print("nilではない")
} else {
    print("nil")
}

Swiftを学び始めたときにん??となる箇所だと思いますが、他の言語と比較することで学びやすくなるかと思います。

Comuputedプロパティについて

swiftは他の言語とは少し違い、基本的にはgetter/setterが必要ない言語です。

ただ、設定するときに値を加工したりする場合はComuputedプロパティというものがあります。(名前は最近知りました)

今回はそんなComuputedプロパティについて説明しようかと思います。

Comuputedプロパティとは

Comuputedプロパティを説明する前に他の言語でのgetter/setterはこんな感じではないでしょうか??

例えば、Javaでは下記ですよね!

public class Product {
    private String name;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

public class Phone {

    Product p = new Product();
     
    p.setName("携帯電話");
        
    System.out.println(p.getName()); //携帯電話
}

このようにgetNameメソッドsetNameメソッドを準備して、必要に応じて、値の取得と設定をメソッドを通じて行います。

つまり、決まった値を保持しておらず、インスタンスが生成される度に値を取得、設定することになります。

では、Swiftではどうでしょうか?

class Product {
    var companyName: String
    
    init(companyName) {
        self.companyName = companyName
    }

    var name:String {
        // 値を取得するときに呼ばれる
        get {
            return companyName
        }
        // 値を設定するときに呼ばれる
        set {
            companyName + "の携帯電話"
        }
    }
}

class Phone {

    let p = Product();
    // setが呼ばれる
    p.name = "A社"
    // getが呼ばれる
    println(p.name) //A社の携帯電話
}

上記のnameプロパティに値を設定するときはset、値を取得するときはgetが呼ばれます。

また、getのみを記載すると読み取り専用のプロパティとなります。

上記のようにせず、別で取得用や設定用のメソッドを作れば同じ動きにすることはできますが、変数の定義を見るとすぐにgetter/setterが見れるのは個人的にわかりやすいと思います。

willSet/didSetについて

Swiftではget・set意外にwillSet/didSetというものがあります。

どういものかみていきましょう!

class Staff {
    var salary: Int { 
        willSet {
            print(salary)// 結果 1000
        }
        didSet {
            print(salary)// 結果  1500
        }
    }
    var worked: Int      //働いた時間
    
    init( _ salary: Int) {
        self.salary = salary
    }
}

class Resutaurant {
    let kuma = Staff(1000)
 kuma.salary = 1500
}

流れは今までみてきた例と同じですが、違いはsalarywillSet/didSetが定義されています。

willSetは値が変わる前の処理を書き、didSetは値が変わった後の処理を書きます。

どんなときに使うかというとdidSetは値が変更したらラベルを変更するなどで使うかもしれません。

willSetは使ったことがないのですが、変更前の値を別の変数で保存しておくなどが考えられます。

Comuputedプロパティとは少し違う話にはなってしまいますが、定義の下に処理がかけるのはComuputedプロパティと同じように見やすく使うこともあるかもしれないので、 参考になると幸いです。

NavigationBarの下にオブジェクトが隠れるのを防ぐ

今回は小ネタです。

最近出くわしたNavigationBarの下にViewが隠れてしまうのを回避する方法です。

iPhone8では大丈夫だったのですが、iPhone Xsでは思いっきり隠れていました。

Viewの位置をソースコードで定義していたので、iPhone Xsのバージョンの位置を追加で定義するのは面倒だと思い、調べてみました。

ちなみにそのViewのTopはSafeareaのTopと同じ位置にしていましたが、うまく表示されませんでした・・・

Storyboardで該当のViewControllerを選択して、Attributes Inspectorを開きます。

Extend EdgesにあるUnder Top Barsのチェックを外すと隠れずに表示されます。

f:id:kumaskun:20201013022749p:plain

これで位置をソースコードで定義する必要もなくなり楽になりました。

この方法は知らなかったので、損してたなあと思いました。

個人的にはStoryBoardでできることはできるだけ設定してしまい、必要な箇所だけコーディングする派なので、理にかなっている方法だと思っています。

Xcodeでのオブジェクトの上下配置について

今回はStroyboardでオブジェクトを配置するときに少しつまずいた上下配置について説明しようと思います。

オブジェクトの配置

XcodeではStroyboardでオブジェクト(ここではViewやボタンなどのパーツのこと)を配置して画面を作ります。

ソースコードでUIを作ることもありますが、コード量が膨大になってしまうので、Stroyboardを使うことが多いです。

例えば、下記のUIの場合で説明します。

f:id:kumaskun:20201011022413p:plain

左側はDocument Outlineといって、オブジェクトの階層を示しています。

1番上に画面いっぱいのViewがあり、その下にFakeLoginViewLoginFormViewという2つのViewが等間隔で配置されています。

FakeLoginViewの下の階層にはラベルが配置されています。

このように配置することで右のような画面が出来上がります。

Constraintsというオブジェクトの位置を決める制約がありますが、ここでは省略します。

オブジェクトの上下配置

では、先ほど使ったイメージのFakeLoginViewを場合によっては別のViewにしたい場合はどうするでしょうか?

別のUIを作れば楽かもしれないですが、1つのUIで管理する方が初期コストはかからないですよね。

今回は初期表示の場合はFakeLoginViewを表示させて、RealLoginViewは場合によって表示させるようにし、これらを1つのViewControllerで管理します。

まずは下記のように配置します。

f:id:kumaskun:20201011023616p:plain

そうすると、下記のようなイメージになってしまいます。

f:id:kumaskun:20201011023801p:plain

FakeLoginViewは消えて、RealLoginViewが前面に表示されてしまします。

これだと、初期表示の場合はRealLoginViewが表示されます。

これはなぜかというとRealLoginViewFakeLoginViewの前面に位置しているからです。

Document Outlineの同じ階層のオブジェクトは下になるほど前面に表示されます。

f:id:kumaskun:20201011024519p:plain
下の方が前面に表示される

なので、対処方法としてはRealLoginViewFakeLoginViewより上に持っていく必要があります。

やり方としては手動で上に持っていくでも可能ですが、 Xcode メニューでもできます。

移動させたいオブジェクトをダブルクリックした状態で、Xcode メニューのEditor > Arrangeを選択して、Send to Backを選択すると選択したオブジェクトが背面に移動します。

ダブルクリックしないとできないので注意です。

f:id:kumaskun:20201011025129p:plain

こうすることで下記のイメージになりました。

f:id:kumaskun:20201011025317p:plain

Document OutlineではRealLoginViewFakeLoginViewより上に配置され、イメージでもFakeLoginViewが表示されています。

このようにiOS開発ではUIの階層があり、これを知らないとUIを作成するのに戸惑うこともあるので、あまり時間をかけずに開発するために知っておく必要があると感じました。

クロージャーの[weak self]について

今回は、クロージャーの[weak self]について書いていこうと思います。

なぜこんな書き方をするのか調べてみると、クロージャーがselfを弱参照し、クロージャーとselfの循環参照を防ぐとの記載がありました。

これを見て??って思いよくわからなかったので、少し調べていきたいと思います。

前提となる参照カウンタなどについては下記で触れています。見ておくと理解が深まるかもしれません。

値型(struct)と参照型(class)について

循環参照とは?

まずは、循環参照について調べていきたいと思います。

循環参照とは、インスタンス間でお互いを強参照しあった場合に参照カウントが0にならず、メモリ上にインスタンスが残り続けてしまう状態のことです。

Swiftでは構造体や列挙隊以外は基本的に強参照です。

class Human {
    let name: String
    var money: Money?

    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

class Money {
    let amount: Int
    var owner: Human?

    init(amount: Int) {
        self.amount = amount
        print("\(amount) is being initialized")
    }

    deinit {
        print("\(amount) is being deinitialized")
    }
}

上記のような2つのクラスがあるとします。初期化すると以下のようなイメージになります。

var kuma: Human? = Human(name: "kuma") // kumaの参照カウントが+1
var money: Money? = Money(amount: 1000) // moneyの参照カウントが+1

f:id:kumaskun:20200927122251p:plain
初期化後のイメージ

この状態で循環参照を実行します。イメージは下記です。

kuma?.money = money // moneyの参照カウントが+1
money?.owner = kuma // kumaの参照カウントが+1
kuma = nil // kumaの参照カウントが-1
money = nil // moneyの参照カウントが-1

f:id:kumaskun:20200927122834p:plain
循環参照のイメージ

循環参照を行うと参照カウントが0にならず、kumamoneydeinitされません。結果的にメモリリークを引き起こします。

循環参照しないためには?

では、循環参照しないためにはどうすればいいでしょうか?

循環参照しないためには参照カウントを増やさなければいいのです。 そのために弱参照(weak reference)アンオウンド参照(unowned reference)の2種類の参照方法があります。

弱参照は参照するインスタンスが後からnilになる場合に使用します。また、通常のOptionalと同様にアンラップが必要になります。

一方、アンオウンド参照は設定されたらnilになることがない(インスタンスの存続期間が一方またはお互いに依存する)場合に使用します。

class Human {
    let name: String
    var money: Money?

    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

class Money {
    let amount: Int
    weak var owner: Human? //weak(弱参照にする)

    // 省略
}

上記のようにどちらかの参照をweakもしくはunownedにします。これによって循環参照は解消され、不要なインスタンスは破棄されます。

var kuma: Human? = Human(name: "kuma")  // kumaの参照カウントが+1
var money: Money? = Money(amount: 1000)  // moneyの参照カウントが+1
kuma?.money = money  // moneyの参照カウントが+1
money?.owner = kuma  // kumaの参照カウントは変わらない
kuma = nil   // kumaの参照カウントが-1
money = nil  // moneyの参照カウントが-1

kuma = nil  // kumaの参照カウントが-1, kumaのdeinitが呼ばれ、moneyの参照カウントが-1
money = nil  // moneyの参照カウントが-1, moneyのdeinitが呼ばれる

イメージは下記です。

f:id:kumaskun:20200927125149p:plain

クロージャの循環参照

クロージャインスタンスなので、クロージャ内でインスタンスを利用した際に循環参照が起こる場合があります。

クラスのプロパティにクロージャを割り当て、その中でselfを参照する場合です。実際に見ていきましょう!

class Human {
    let name: String
    let blood: String

    init(name: String, blood: String) {
        self.name = name
        self.blood = blood
    }

    lazy var getHumanInfo: () -> String = {
        return "\(self.name) \(self.blood)" // selfの参照カウントが+1
    }

}

var kuma: Human? = Human(name: "kuma", blood: "A")
print(john?.getHumanInfo()) // getHumanInfoの参照カウントが+1とselfの参照カウントが+1の合計+2になる
kuma = nil // Human内で循環参照が起きているのでdeinitが呼ばれない

上記の場合は、HumanインスタンスgetHumanInfoインスタンスを参照しており、getHumanInfoインスタンスからHumanインスタンスを参照しているので、循環参照が発生しHumanインスタンスが破棄されません。

循環参照を避けるために、クロージャ内でインスタンスを利用する場合にはキャプチャリストを用います。 キャプチャリストでは弱参照・アンオウンド参照を指定することができるので、 クロージャの解放状況に依存せずにクラスのインスタンスを解放することが可能になります。

class Human {
    let name: String
    let blood: String

 // 省略

    lazy var getHumanInfo: () -> String = { [weak self]  _ in 
        return "\(self.name) \(self.blood)" // selfの参照カウントは変わらない
    }

}

var kuma: Human? = Human(name: "kuma", blood: "A")
print(john?.getHumanInfo()) // getHumanInfoの参照カウントが+1
kuma = nil  // deinitが呼ばれる

キャプチャリストとは上記の場合、[weak self]のことです。inは必ず必要です。

メモリ管理でのバグは調査が大変だと思うので、困ったら[weak self]を使い、[unowned self]を使う場合は設計を確認して使うのがいい気がしました。

参照

Swiftでなんで[weak self]するのか?

Swiftのメモリ管理を知る