Karakuri.com

ベンチャー企業で働くソフトウェアエンジニアの技術録

抽象クラス(Abstract)とインターフェース(Interface)の違いと実装の使い分けについて

C#やJavaなどのオブジェクト指向型プログラミング言語に用意されている抽象クラスとインターフェースですが、コード的な違いは理解していても使い分けまでできているケースは意外と少ないです。中級未満のエンジニアだと、そもそも抽象クラスやインターフェースを使うことすらしない場合も。決して自分が完璧に理解し、使いこなしていると自負しているわけではないのですが、中級者レベルの理解と使い分けについてまとめます。

抽象クラスとインターフェースの違い

文法的(コード)な違い

  • インターフェースは実装を持てないが、抽象クラスは実装を持つことができる
  • 具象クラスはインターフェースを複数実装できるが、抽象クラスは1つしか継承することができない

教科書的には上記が主な違いになります。ただ、上記の文法的な違いだけを根拠に使い分けているとコードは保守性、拡張性を落としてしまいます。

  • 異なる実装の処理を同じインスタンスとして使用したいからインターフェースを使う
  • 処理を共通化したいから抽象クラス使う
  • 複数使いたいからインターフェースを使う

この考えはオブジェクト指向ではありません。

続きを読む

SwiftにはByte型がないので戸惑うけれどUInt8がバイトやバイト配列を担っています

むかしむかしのSwiftにはByte型があったと思うのですが、あるとき突然廃止されました。Byte型の配列など使って低級の処理をしていたアプリやプログラムはコンパイルすらできない事態に。C#やJavaからやってきた人も一瞬戸惑うのではないでしょうか。どう調べれば良いかも分からないのですが、結論から言うとUInt8型がByte型に相当するっぽいです。

Swiftのバイト配列

そもそも高級言語でバイト配列を触るなという話かもしれませんが、IoTやっていると触る必要が出てくるものですよね。相手がバイト配列でコマンドを定義していたりするので。例えばC#だと下記のようなバイト配列を定義できます。

var byteArray = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }

これをSwiftで書くとこうなります。

let byteArray: [UInt8] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]

こういう痒い所の情報がなかなか出てこないSwiftはまだまだ辛いですなあ。

Swiftでバイト配列(UInt8配列)の変数どうしで論理演算XORを計算する方法

Swiftで[UInt8]のままXORをする必要がありました。しかしSwiftのXOR演算子は「^」であり、下記のように書いてもコンパイルエラーとなります。

let first: [UInt8] = [0xff, 0x10, 0xa0]
let second: [UInt8] = [0x01, 0x02, 0x03]
let result: [UInt8] = first ^ second

まあ、当然ですね。「^」演算子を使う場合はInt型の変数である必要があります。

ループでUInt8ペアを全てXORすればいい

かといって、[UInt8]を10進数に変換して演算子を使ってXORすると、Int型から[UInt8]に戻すのが大変です。しばらく格闘してみましたが、挫折しました。で、息抜きしていたら「UInt8ペアをXORしていけばいいやん」と気づきました。

public static func xor(first: [UInt8], second: [UInt8]) -> [UInt8] {
    var answer = [UInt8](repeating: 0x00, count: first.count)
        
    for index in 0..<first.count {
        answer[index] = first[index] ^ second[index]
    }
        
    return answer
}

いやほんとプログラミングの成果って時間に比例しないですねー。息抜きしたり休んだほうが圧倒的に短い時間で仕事終わったりしてしまう。。。

MacOSにHomebrewでHttpキャプチャソフトFiddlerはインストールできなくなったのでMonoで起動しましょう

HttpでAPIを叩くときに重宝するのがFiddlerです。Windowsアプリ開発では使わせていただいているのですが、iOSアプリ開発でも使えるのでしょうか。どうもMacOSでも使っている人が結構いるみたいなのでインストールすることにしました。

Homebrewでインストールできるらしいが…

ググるとHomebrewで簡単にインストールできるそうです。Homebrewでインストールして使っているという記事がたくさん出てきます。しかし実際にインストールを試みると…

iMac:$ brew cask install fiddler
Error: Cask 'fiddler' is unavailable: No Cask with this name exists.

インストールできない。そんなアプリは存在しないと言わんばかりです。おかしいな…。

続きを読む

Githubにpushできなくなってfatal: could not read Username for 'https://github.com'とか言われる

C#で.Net Frameworkなアプリケーション開発にはVisual StudioとGithub Extension for VIsual Studioを使っています。最近は自宅作業もなかったため、久しぶりに家の環境でpushしてみたら

fatal: could not read Username for 'https://github.com' No such file or directory

と言われてpushできない状態に… orz

状況を調べてみる

Cloneとかはできるので、Usernameが読めないとか、ファイルやディレクトリが見つからないというのは意味がわかりませんね。それでこのエラーメッセージをググって見たのですが、特にこれといった情報には出会えず…。
qiita.com
ただ、この人は2014年にGitのバグでpushできない状態になっていると言っています。今は当時から比較するとメジャーアップデートもされているので、この不具合が原因ってことはなさそうです。ということは、壊れた?

続きを読む

XcodeとSwiftでUILabelのテキストのハイパーリンク化は気軽にはできない

HTMLライクなデザインでテキストにハイパーリンクを付けることになりました。

各項目を入力して送信ボタンを押してください。何か分からないことがありましたら、こちらからお問い合わせください

こんなケースですね。アンダーバーだけハイパーリンク化しないといけなかったんですね。これをUILabelを使って実現できるか色々調べて試してみました。

NSMutableAttributedStringを使う

NSMutableAttributedStringというクラスがあって、Stringに様々な属性を付加することができます。UILabelやUITextViewにはatributedTextというプロパティがあるので、このプロパティにNSMutableAttributedStringインスタンスをセットするとアンダーラインや文字色など変化あるテキストを表示することができます。WindowsやAndroidだとマークアップ言語に記述できるのですが、iOSだとコードビハインドで遅れていますね。まあ、しょうがないです。

let textView = UITextView()
textView.isSelectable = true
textView.isEditable = false

let text = "各項目を入力して送信ボタンを押してください。何か分からないことがありましたら、こちらからお問い合わせください。"
        
let attributedString = NSMutableAttributedString(string: text)
let range = NSString(string: text).range(of: "こちらからお問い合わせください")

attributedString.addAttribute(NSAttributedStringKey.link, value: "https://www.yahoo.co.jp/", range: range)
attributedString.addAttribute(NSAttributedStringKey.underlineStyle, value: 1, range: range)
        
textView.attributedText = attributedString

これで「こちらからお問い合わせください」だけハイパーリンク化されます。しかしこれはUITextViewを使った場合です。

続きを読む

SwiftでObjective-Cのポインタ型を書き換えるときのUnsafeMutablePointer<T>について

とあるSDKを使ってiOSアプリを開発しているのですが、いただいたSDKのドキュメントに書かれているサンプルコードがObjective-Cでした。僕はObjective-Cは嫌いなのでSwiftに書き換えます。するとこんな関数がありました。

(id)method:(int*)arg;

Int型のポインタです。Swiftにはない概念ですね。これどうやってSwiftから呼び出せばいいんでしょうか。

Xcodeのエラーを見てみる

とりあえずこう書いてみます。

let value = 1
method(value)

当然エラーとなります。XcodeはUnsafeMutablePointer<Int>を使えとか言ってきます。なんじゃそりゃ。
UnsafeMutablePointer - Swift Standard Library | Apple Developer Documentation

続きを読む

SwiftでiOSアプリのファイルシステムのディレクトリの存在チェックを行う

iOSアプリのDocumentsディレクトリにログを出力しようと思い、Logディレクトリを作成しました。当然このディレクトリはアプリが初めて起動したときに作成され、以降はディレクトリ作成を行う必要はありません。でもSwiftでディレクトリの存在チェックって情報があまりないんですよね。

ファイルが存在するかどうかを調べる

ググって出てくるのは任意のファイルが存在しているかどうか調べる方法です。

FileManager.default.fileExists(atPath: filePath)

でもなぜかdirectoryExistsというメソッドは存在しません。え、これどうやってディレクトリの存在チェックすればいいんですかね。

続きを読む

Swift4でDateの日時を任意のフォーマットでStringで表示する

Swiftの日時を表示するのはDateクラスで簡単に実装できます。

// 2018/01/12 00:00:00
print(Date())

しかしFormatを指定するとなると結構行数を使ってしまうんですよね。

let format = DateFormatter()
format.timeStyle = .short
format.dateStyle = .short
format.locale = Locale(identifier: "ja_JP")
// 2018/01/12 00:00
print(format.string(from: Date()))

Dateクラスを拡張する

毎回書くのは冗長ですし、表記を変えたいとなったときに死ぬのでDateクラスを拡張してしまいましょう。

import Foundation

public extension Date {
    public func toString() -> String {
        let format = DateFormatter()
        format.timeStyle = .short
        format.dateStyle = .short
        format.locale = Locale(identifier: "ja_JP")
        return format.string(from: self)
    }
}

これで楽になりました。.Net Frameworkが

// 2018/01/12 00:00:00
DateTime.Now.ToString();

と書けるように、今回拡張したことで

// 2018/01/12 00:00
Date().toString()

と書くことができるようになりました。

続きを読む

Swift4で任意のDateの日時から日の始まりである0時0分のDateの日時を作る

iOSアプリ開発においてデータベースのデータから本日登録されたデータだけを取り出す必要があったのですが、そのためには今日の0時0分のDateを作らなければなりません。例えば.Net Frameworkでは下記のように作成することができます。

var now = DateTime.Now;
var date = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);

ところがSwiftのDateクラスにはそのようなイニシャライザはありません。また、Dateクラスから時間や分をInt型の値として取り出すこともできないようです。

Calenderクラスと組み合わせて作る

DateクラスとNSDateクラスばかり調べていたので時間かかってしまいました。結論から言うと下記のようにして今日の0時0分を作ることができます。

Calendar(identifier: .gregorian).startOfDay(for: Date()))

Calenderクラスを使うのですね。上記の例ではDateインスタンスを新規で生成しているので今日になりますが、別の日のDateインスタンスを使えば任意の日時の0時0分を作ることができます。.Net FrameworkはDateTime型で色々できるのでNSDateやDateは貧弱で辛いなあと思っていたのですが、Calenderクラスと組み合わせることでDateTimeと同等の機能が用意されているようです。