ためすう

Swift、Objective-Cで辞書型をログに出力する方法

2019-01-07

目的

iPhone アプリを開発している時、辞書型のデータの中身を確認します。

Swift、Objective-Cで辞書型をログに出力する方法について書きます。

Swift で辞書型を確認する

let abc:[String: Any] = [
    "a": 99,
    "b": "ggg"
]
print(abc)

出力

["b": "ggg", "a": 99]

Objective-C で辞書型を確認する

NSDictionary *ab = [NSDictionary dictionaryWithObjectsAndKeys:
                    @"99", @"a",
                    @"ggg", @"b",
                    nil
                    ];
NSLog([NSString stringWithFormat:@"ABC: %@", [ab description]]);

出力

2018-12-02 14:34:42.115787+0900 exprement-objc[50570:4039196] ABC: {
    a = 99;
    b = ggg;
}

Objective-C はログを出力するだけでも一苦労でした。

参考

XcodeのRun Scriptを設定する方法

2019-01-06

目的

XcodeのRun Script を設定する方法について書きます。

ビルド時に環境ごとに読み込む設定ファイルを変えたい時などに利用できます。

スクリプトの用意

プロジェクトを選択し、「Build Phases」を選びます。

そこで 「Run Script」を追加します。

echo "Hello World"

今回は文字を表示するだけのスクリプトですが、設定ファイルを読み変えたりするときに利用できそうです。

参考

条件記述の分解 (リファクタリング-p238)

2018-12-24

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「条件記述の分解」について書きます。

変更前

<?php

date_default_timezone_set('Asia/Tokyo');

class Dummy
{
    private $_winterRate = 0.6;
    private $_summerRate = 0.2;
    private $_winterServiceCharge = 250;

    const SUMMER_START = '2018/07/01 00:00:00';
    const SUMMER_END = '2018/08/31 23:59:59';

    public function calcCharge($quantity)
    {
        $now = time();
        if (strtotime(self::SUMMER_START) > $now ||
            strtotime(self::SUMMER_END) < $now) {
            return $quantity * $this->_winterRate * $this->_winterServiceCharge;
        }

        return $quantity * $this->_summerRate;
    }
}

$d = new Dummy();
echo $d->calcCharge(10) . "\n";

変更後

<?php

date_default_timezone_set('Asia/Tokyo');

class Dummy2
{
    private $_winterRate = 0.6;
    private $_summerRate = 0.2;
    private $_winterServiceCharge = 250;

    const SUMMER_START = '2018/07/01 00:00:00';
    const SUMMER_END = '2018/08/31 23:59:59';

    public function calcCharge($quantity)
    {
        if ($this->notSummer()) {
            return $this->winterCharge($quantity);
        }

        return $this->summerCharge($quantity);
    }

    private function notSummer()
    {
        $now = time();
        return (strtotime(self::SUMMER_START) > $now) ||
            (strtotime(self::SUMMER_END) < $now);
    }

    private function summerCharge($quantity)
    {
        return $quantity * $this->_summerRate;
    }

    private function winterCharge($quantity)
    {
        return $quantity * $this->_winterRate * $this->_winterServiceCharge;
    }
}

$d = new Dummy2();
echo $d->calcCharge(10) . "\n";

フィールドによるサブクラスの置き換え (リファクタリング-p232)

2018-12-23

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「フィールドによるサブクラスの置き換え」について書きます。

「フィールドによるサブクラスの置き換え」 について

定数データを返すメソッドだけが異なるサブクラスがある。

変更前

<?php

abstract class Person
{
    abstract function isMale();
    abstract function getCode();
}

class Male extends Person
{
    public function isMale()
    {
        return true;
    }

    public function getCode()
    {
        return 'M';
    }
}

class Female extends Person
{
    public function isMale()
    {
        return false;
    }

    public function getCode()
    {
        return 'F';
    }
}

変更後

<?php

class Person2
{
    private $_isMale;
    private $_code;

    protected function __construct($isMale, $code)
    {
        $this->_isMale = $isMale;
        $this->_code = $code;
    }

    public static function createMale()
    {
        return new self(true, 'M');
    }

    public static function createFemale()
    {
        return new self(false, 'F');
    }

    public function isMale()
    {
        return $this->_isMale;
    }

    public function getCode()
    {
        return $this->_code;
    }
}

$kent = Person2::createMale();
echo (int)$kent->isMale() . "\n";
echo $kent->getCode() . "\n";

$ann = Person2::createFemale();
echo (int)$ann->isMale() . "\n";
echo $ann->getCode() . "\n";

State/Strategyによるタイプコードの置き換え (リファクタリング-p227)

2018-12-23

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「State/Strategyによるタイプコードの置き換え」について書きます。

「State/Strategyによるタイプコードの置き換え」 について

クラスの振る舞いに影響するタイプコードがあるが、サブクラス化はできない。

タイプコードがそのオブジェクトの生存期間中に変化するか、サブクラス化が不都合な理由が別にあるときに適用できます。

変更前

<?php

class Employee
{
    private $_type;

    private $_monthlySalary;
    private $_commission;
    private $_bonus;

    const ENGINEER = 0;
    const SALESMAN = 1;
    const MANAGER = 2;

    public function __construct($type)
    {
        $this->_type = $type;
    }

    public function payAmount()
    {
        $this->_monthlySalary = 2000;
        $this->_commision = 1000;
        $this->_bonus = 3000;

        switch ($this->_type) {
            case self::ENGINEER:
                return $this->_monthlySalary;
            case self::SALESMAN:
                return $this->_monthlySalary + $this->_commission;
            case self::MANAGER:
                return $this->_monthlySalary + $this->_bonus;
            default:
                throw new Exception('不正な従業員');
        }
    }
}

$e1 = new Employee(2);
echo $e1->payAmount() . "\n";

変更後

<?php

abstract class EmployeeType
{
    const ENGINEER = 0;
    const SALESMAN = 1;
    const MANAGER = 2;

    abstract function getTypeCode();

    public static function newType($code)
    {
        switch ($code) {
            case self::ENGINEER:
                return new Engineer();
            case self::SALESMAN:
                return new Salesman();
            case self::MANAGER:
                return new Manager();
            default:
                throw new Exception('不正な従業員コード');
        }
    }
}

class Engineer extends EmployeeType
{
    public function getTypeCode()
    {
        return self::ENGINEER;
    }
}

class Salesman extends EmployeeType
{
    public function getTypeCode()
    {
        return self::SALESMAN;
    }
}

class Manager extends EmployeeType
{
    public function getTypeCode()
    {
        return self::MANAGER;
    }
}

class Employee2
{
    private $_type;

    private $_monthlySalary;
    private $_commission;
    private $_bonus;

    public function __construct($type)
    {
        $this->setType($type);
    }

    public function getType()
    {
        return $this->_type->getTypeCode();
    }

    public function setType($type)
    {
        $this->_type = EmployeeType::newType($type);
    }

    public function payAmount()
    {
        $this->_monthlySalary = 2000;
        $this->_commision = 1000;
        $this->_bonus = 3000;

        switch ($this->getType()) {
            case EmployeeType::ENGINEER:
                return $this->_monthlySalary;
            case EmployeeType::SALESMAN:
                return $this->_monthlySalary + $this->_commission;
            case EmployeeType::MANAGER:
                return $this->_monthlySalary + $this->_bonus;
            default:
                throw new Exception('不正な従業員');
        }
    }
}

$e2 = new Employee2(2);
echo $e2->payAmount() . "\n";

サブクラスによるタイプコードの置き換え (リファクタリング-p223)

2018-12-16

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「サブクラスによるタイプコードの置き換え」について書きます。

「サブクラスによるタイプコードの置き換え」 について

クラスの振る舞いに影響を与える不変のタイプコードがある。

変更前

<?php

class Employee1
{
    private $_type;

    const ENGINEER = 0;
    const SALESMAN = 1;
    const MANAGER = 2;

    public function __construct($type)
    {
        $this->_type = $type;
    }
}

変更後

<?php

abstract class Employee2
{
    const ENGINEER = 0;
    const SALESMAN = 1;
    const MANAGER = 2;

    abstract public function getType();

    public static function create($type)
    {
        switch ($type) {
            case self::ENGINEER:
                return new Engineer();
            case self::SALESMAN:
                return new Salesman();
            case self::MANAGER:
                return new Manager();
            default:
                throw new Exception('タイプコードの値が不正');
        }
    }
}

class Engineer extends Employee2
{
    public function getType()
    {
        return Employee2::ENGINEER;
    }
}

class Salesman extends Employee2
{
    public function getType()
    {
        return Employee2::SALESMAN;
    }
}

class Manager extends Employee2
{
    public function getType()
    {
        return Employee2::MANAGER;
    }
}

$e2 = Employee2::create(1);
echo $e2->getType() . "\n";

クラスによるタイプコードの置き換え (リファクタリング-p218)

2018-12-16

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「クラスによるタイプコードの置き換え」について書きます。

「クラスによるタイプコードの置き換え」 について

振る舞いに影響しない数字のタイプコードを持つクラスがある。

この数字をクラスで置き換えれば、コンパイラはそのクラスについて型チェックができます。

変更前

<?php

class Person
{
    private $_bloodGroup;

    public function __construct($bloodGroup)
    {
        $this->_bloodGroup = $bloodGroup;
    }

    public function getBloodGroup()
    {
        return $this->_bloodGroup;
    }
}

// 呼び出し
$person = new Person(0);
echo $person->getBloodGroup() . "\n";

$person = new Person(2);
echo $person->getBloodGroup() . "\n";

変更後

<?php

class BloodGroup
{
    const O = 0;
    const A = 1;
    const B = 2;
    const AB = 3;

    private static $_values = [
        self::O,
        self::A,
        self::B,
        self::AB,
    ];

    private $_code;

    public function __construct($code)
    {
        $this->_code = $code;
    }

    public function getCode()
    {
        return $this->_code;
    }

    private static function code($arg)
    {
        return $this->_values[$arg];
    }
}

class Person2
{
    private $_bloodGroup;

    public function __construct(BloodGroup $bloodGroup)
    {
        $this->_bloodGroup = $bloodGroup;
    }

    public function setBloodGroup(BloodGroup $arg)
    {
        $this->_bloodGroup = $arg;
    }

    public function getBloodGroupCode()
    {
        return $this->_bloodGroup->getCode();
    }
}

// 呼び出し
// PHP ではメンバ変数にクラスを設定できない
// BloodGroup クラスの呼び出し方を検討する
//
// Person クラスに BloodGroup クラスを注入する
$blood = new BloodGroup(BloodGroup::A);
$person = new Person2($blood);
echo $person->getBloodGroupCode() . "\n";

$blood = new BloodGroup(BloodGroup::B);
$person = new Person2($blood);
echo $person->getBloodGroupCode() . "\n";
<?php

データクラスによるレコードの置き換え (リファクタリング-p217)

2018-12-15

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「データクラスによるレコードの置き換え」について書きます。

「データクラスによるレコードの置き換え」 について

この外部要素を扱うインターフェースを持つクラスを作ることが有効です。

見出し1 見出し2 見出し3
item1 center right
item2 abc def

<?php

class Dummy
{
    private $items = [];

    public function __construct($data)
    {
        $this->items = $data;
    }

    public function getItems()
    {
        return $this->items;
    }
}

// 古いプログラミング環境のレコード構造
$data = [
    [
        'item1', 'center', 'right'
    ],
    [
        'item2', 'abc', 'def',
    ],
];

// Dummy クラスを介してやり取りする
$dummy = new Dummy($data);
print_r($dummy->getItems());

Swift の第一引数がある関数を Objective-C で呼び出すときの注意

2018-12-09

目的

Swift で第一引数がある関数を定義して、 Objective-C で呼び出すとき何度か嵌ったのでメモしておきます。

嵌った例

Swift の関数

@objcMembers class Calc: NSObject {
    static func add(a:Int, b:Int) -> Int {
        return a + b
    }
}

Objective-C 側

[Calc add:2 b:3];

下記のようなエラーが発生しました。

No visible @interface for 'Calc' declares the selector 'add:b:'

解決した方法

Swift の関数の第一引数にラベルをつけない指定をしました。

Objective-C の場合は第一引数にラベルをつけないため、_ を指定する必要があります。

@objcMembers class Calc: NSObject
    static func add(_ a:Int, b:Int) -> Int {
        return a + b
    }
}

Objective-C 側は変えずに下記で呼び出せます

[Calc add:2 b:3];

@objcMembers について

class の前の @objcMembers は、 Swift のコードを Objective-C のコードを呼び出すために必要です。

参考

Objective-C の import について

2018-12-09

目的

iOS アプリを開発しているとき、import の記述方法が2つあるので調べました。

例えば、 Hoge というモジュールを読み込みたいとします。

@import について

@import Hoge;

Hoge 配下のモジュールが利用可能になります。

プロジェクト > Build Settings > Enable Modules が Yes になっている必要があります。

#import について

#import "<Hoge/Moge.h>"

Hoge 配下の Moge を読み込みます。

紛らわしいですが、下記でも呼べます。

@import Hoge.Moge;

参考