Twitter 界隈の IT エンジニア

Objective-C のツィには反応があって嬉しいんだが、逆にこれに対する反応が薄いのがなんかなあという感じ。

誰かが言っていたが、Twitter 界隈の「IT エンジニア」の8割は IT エンジニアではない説は本当かもね。

「VS Code どう使っている?」と言ったら、普通、もっと反応あるでしょ。

ちょっと前は「emacs vs vi」で盛り上がっていた業界なのだから。

 

 

Objective-C 再入門

普通に iPhone アプリを使うなら swift でいいんだろうが、既存の C/C++ ライブラリを組み込んで使うような場合などは、Objective-C も検討せねばならないだろう。

特にレガシーなプロジェクトなぞ、本体が Objective-C で書かれているので、swift に移植するにせよ、ある程度の Objective-C の理解は必須だろう。

なぜ Objective-C はとっつきにくいのか?

ところで、Objective-C のクセの強さは、万人の知るところだが、これは歴史的背景を考えると理解しやすいかもしれない。

C にオブジェクト指向の思想を取り込むために、大雑把に二つのアプローチがあったようだ。

一つは言語自体の設計を変えてしまう方法。これが現在の主流で、 C++ の源流となっている。

もう一つは、C のコンパイラー自体には大きな変更を加えず、文法的な表記に特殊仕様を入れ、パース時にこれを解釈して、オブジェクトを取り扱えるようにする方法。Objective-C は、どちらかといえば、こちらだろう。

だから、C++ 感覚で取り組もうとすると訳のわからないことになってしまう。

また、Objective-C がとっつきにくいのは、ビギナー向けのほとんどのサンプルが Mac や iPhone の GUI アプリだという点。これだと、Objective-C の勉強というよりも cocoa などの勉強になってしまい、Objective-C の言語の本質みたいなものが掴みにくい。

サンプル

というわけで、GUI 一切なしのコマンドラインプログラムを試しに書いてみた。


#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <stdio.h>
// Person クラスの宣言
@interface Person : NSObject {
    NSString *name;
}
- (void)setName :(NSString *)Name;
- (void)printName;
@end
// Person クラスの実装
@implementation Person : NSObject
- (void)setName :(NSString *)Name {
    name = Name;
}
- (void)printName {
    printf("My name is %s \n", [name UTF8String]);
}
@end
// 実行プログラム
int main(void) {
    id person;
    person = [[Person alloc] init];//Person *person = new Person(); などとはしない
    [person setName:@"akiba"];
    [person printName];
    return 0;
}

これを Xcode 上で実行するとこんな感じになる。

Person クラスを実体化して、フィールド変数の name をセットした後、ゲッター経由でこの値を取得・表示するというよくあるプログラムだ。

GUI がないから、ワンファイルにすっきりと収まっているが、(Java や C++ に慣れた目から見ると)違和感を感じるところがちょいちょいある。

まず、どうということはないクラス Person でも NSObject を継承している点。またクラスを定義するときに@ディレクティブを多用している。

Objective-C の「C のコンパイラー自体には大きな変更を加えず、文法的な表記に特殊仕様を入れ、パース時にこれを解釈して、オブジェクトを取り扱えるようにする」という歴史的経緯に由来する特徴がよく出ているのではないかと思う。

ところで、最も違和感のあるのはこの箇所だろう。

    person = [[Person alloc] init];

オブジェクトに対してメッセージを送ることで機能を発現させているわけだが、これも歴史的な背景があり、 SmallTalk の影響らしい。

ただし、id 型は必ずしも使う必要はなく、

Person *person = [[Person alloc] init];

としても動きます。

また、Objective-C は C の拡張なので、C の関数も当然使える。実際、printf は C と同じように動作している。

その他、注意しておきたいところ

フィールド変数(インスタンス変数)

name のセッターは setName になってますが、フィールド変数の先頭文字を「大文字」にするのはお約束のようです。
ここら辺は Java と一緒でしょうか。

UTF8String がわからん、という声も聞きますが、name 自体は NSString (へのポインタ)です。標準 C の String に変換する必要があるので、ここで変換をかけているわけです。

なお、インスタンス変数の宣言の仕方などについては、この質疑応答が凄まじくよくまとまっています。

m と mm の違い

Objective-C のファイルは .m .mm の二つがあり得ます。
この記事がわかりやすいんですが、会員限定かな。
簡単にいえば、.m が objective-C ファイル本来の拡張子、.mm の方は C++ のファイルを取り込む際に使用する拡張子です。

継承とカテゴリ

オブジェクト指向の特徴の一つは継承だが、Objective-C はここら辺は素直だ。

よくわからんのは「カテゴリクラス」。

Person(hogehoge)

とあった場合、このクラスは元の Person のカテゴリクラスです。

元の場所とは違った場所でメソッドが追加できます。(同じ理由だが分割することもできる)

情報の古い(アップデートされてない)記事が多い

Obj-C ユーザーが少ない理由に、記事がそもそも少ないし、あっても古い(アップデートされてない)というのも一因になっていると思う。

例えば、よく「Obj-C ではクラス変数という概念がない」などと紹介されているが、2016 以降(OS 10.12 〜)はある

公式アナウンスはこちら

使い方は別記事で説明するかもしれないが、@property(class) などとする。
若干クセはあるのだが、普通にクラス変数っぽく使える。あるじゃん。

また、objective-c 自体も文法が(主に 2010 年代に)モダンに改変されており、@property や @synthesize なども使い方が微妙に変わっている。

この図はこちらの記事からお借りしてきました。わかりやすいですね、感謝!

 

多重継承の禁止とプロトコル

継承が出てきたので、書いておくと Obj-C では多重継承、つまり複数のクラスを親クラスにすることができないらしい。

多重継承の代わりにプロトコルで代用しなさいよ、というのがアップルの教え。
サンプルコードなどで

@interface ViewController : NSViewController<NSTableViewDataSource>

という書き方をよく見かけるが、<> 内がプロトコル。


このまま続けてもいいのだが、まとまり悪くなるのでここでいったん切ろう。

ワイもそうだが、IT 業界にいる限り Objective-C だけいじっていればいいというものではない。
というか実際に多い案件は、別の言語だったりする。

だから、この記事は「ちょっと objective-C 思い出すときに読み返す」ときに役にたつ内容にしておく。

その他

@property 表記

アクセサー(ゲッターやセッターのこと)を定義できる。慣れれば便利なのだろうが、Java あたり読み書きしているとすぐにこの規約を忘れる。

この記事読んでね。

クラスエクステンションの () 表記

忘れやすいのがクラスエクステンションの時に使われる () 。

詳しくはこの記事あたり参照。

なんでヘッダーで定義された宣言を .m ファイルでもう一回やる必要があるのか?疑問に思ったら読むといいと思う。

Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
// 名前
@property (nonatomic) NSString *name;
// 名前と年齢をログに出力する
- (void)displayProfile;
@end
Person.m
#import <Foundation/Foundation.h>
@interface Person ()
// 年齢
@property (nonatomic) NSInteger age;
// 名前をログに出力する
- (void)displayName;
// 年齢をログに出力する
- (void)displayAge;
@end
@implementation Person
……
@end

要するに Obj-C には Public だの Private がないため、ヘッダーファイルではなく実体ファイルの方でメソッド・メンバー変数を定義することで外部クラスからのアクセス制限を行っているという理屈です。

Person.h
@interface Person: NSObject
@end

Person.m
@interface Person()
@end

@implementation Person
@end

という構造が見えてますか?

強調して書くとこういう仕組み。

強調して、と書いたがプロジェクトを作成するとき AppDelegate は () をつけて生成してくれますね。

 

 

 

 

 

 

カテゴリの () 表記

ここが紛らわしいと思うのだが、@interface () はクラスエクステンションだけでなくカテゴリでも使われる

ただ、こちらはファイル名自体が ClassA+ClassB.h のようになっているので、それで判断できるかな。

カテゴリーとクラスエクステンション』参照。

 

 

Perl 覚え書き

最近では見かける機会も減ってきた Perl という言語だが、レガシーなプロジェクトでは使われていたりする。

MacOS では標準で搭載されている

ちなみに MacOS では標準で搭載されている。
ターミナルで

which perl

とタイプすると

/usr/bin/perl

と返ってくるはずだ。

バージョンもチェック。

perl --version

とすると

This is perl 5, version 30, subversion 3 (v5.30.3) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2020, Larry Wall
(以下略)

と返ってくる。v5.30.3 だそうです。

ターミナルでハロワ

動作確認。

perl -e 'print "Hello, World\n"'

もちろん Hello, World と表示される。

へえ、動くんだ。

スクリプトファイルを書いて実行

ターミナルで対話モードで使うことは稀だと思うので、スクリプトファイルを書いてみる。

perl といえばテキスト操作だと思うので、まずはフォルダを作る。

(mkdir.pl)
#!/usr/bin/perl

use warnings;
use strict;

print "mkdir test\n";
my $dir = "testdir";
mkdir $dir
  or die " fail: $!";
print "$dir has been created\n";

ワイは、エディタはもっぱら Visual Studio Code を使っているが、cmd + N で新規ファイルを作成して、上のようにタイプし、保存。
実行権限を適当に与えて ./mkdir.pl として実行させるとこのフォルダ階層直下にフォルダをつくってくれた。

確かに便利っすね。

なお、my はローカル変数らしいのだが、perl は歴史的経緯でこれをレキシカル変数というのだそうだ。

Perl について

より本格的に使い始める前にいくつか基本事項。

公式サイトは https://www.perl.org/

本格的に使うのであれば、モジュール CPAN は必須のようだ。
なお、CPAN は Comprehensive Perl Archive Network の略。

ただし、ファイル操作の基本的機能はデフォルトで使えるっぽい。

コメントは # しかないようだ。

複数行でも # を使うのが正式ルールらしい。

ただ、POD 形式というものがあり、

=pod
comment
Perl ignores this line
=cut

とすると解釈時には無視してくれるらしく、デバッグ時にはこれをコメントとして使っても問題はないようだ。

 

 

(続く)

 

ものぐさ RESTEasy -画像データなどを REST っぽく返したい-

『ものぐさ Jersey』的なやつが好評のようなので、その RESTEasy 編。

いきなりで恐縮だが、REST なウェブサービスを作っている際に画像も REST 的に取り扱いたいときがある。
要するに適当なエンドポイントを叩くと画像ファイルが返ってくる、みたいなやつだ。

サーブレットで実現するのが一番簡単なようなんだが、サーブレットを使わない方向でチャレンジする。

リンクした記事は jsp で書いてあるが、ちなみにサーブレットで書き直すとこんな感じ↓になる。

あっさり成功。

 

で、RESTEasy 。
まずは公式ドキュメントのチェック。

レスポンスを適当に加工すれば、なんとかなるだろうくらいに思って調べてみた。

なんだが…

なんでしょう、この突き放された感じ(笑)。

jakarta.ws.rs.core.StreamingOutput を使って自分で実装しろ的なことが書かれている。

いや、そんなことをするくらいならば、サーブレットのやつそのまま使いたいかなあ(困惑)。

アウトプットストリーム系を使って、とりあえずバイト列を返す

他の人がどうやっているかよくわからないのだが、まずはシンプルに攻める。

(公式ドキュメントのサジェスチョンを尊重して)バイト列を生成して、そいつを ByteArrayOutputStream にセットし、さらにそれを Response の中(Entity とかいうみたいだ)に入れ込んで REST っぽく返す、と以下のような感じになる。


@Path("/octet")
public class OctetServer {

 @GET
 @Produces(MediaType.APPLICATION_OCTET_STREAM)
 public Response sendOctet() throws IOException{

  byte[] byteData = "123456789ABCDE".getBytes();

  ByteArrayOutputStream output = new ByteArrayOutputStream();
  output.write(byteData);

  return Response.ok(output.toByteArray(), MediaType.APPLICATION_OCTET_STREAM).build();

 }

}

これで curl で (コンテクスト)/octet を叩くと、もちろん 123456789ABCDE がバイナリで返ってくる。
ブラウザからアクセスすると octet というファイルを落としてくれるが、中身は当然 123456789ABCDE だ。

これで、第一段階はクリア…

と思っていたが、コード見直すと、バイト列をストリームにセットした後、さらにバイト列に戻す、というバカっぽいことをやっている(苦笑)。

バイト列をエンティティとしても同じ

だから、上のサンプルはもうちょっと簡単になる。

具体的には、

return Response.ok(output.toByteArray(), MediaType.APPLICATION_OCTET_STREAM).build();

は、

return Response.ok(byteData, MediaType.APPLICATION_OCTET_STREAM).build();

としても変わらない。

もちろん、その上の2行は全く不要。

とりあえず、バイナリのデータを返すだけであれば、アウトプットストリーム系は不要で返したいデータのバイト列を response のエンティティにセットすればいいだけのようだ。

指定した画像などを返したければ、path parameter を設定してデータベースから検索して云々とやれば、実現できそう。

 

 

H2 database

使われている割に存在感が希薄な H2 database。(WildFly ではデフォで使える)

調べたら、ホームページもあった。
https://www.h2database.com

使い方はそこの Cheat Sheet にまとまっているので、特に付け加えることもないのだが、サーバーモードというのは知りませんでした。
ちゃんと調べてみるものだね。

H2 console を単体で使いたければ、h2-(version).jar を入手して、ダブルクリックで起動、ブラウザ画面が立ち上がる。

pgAdmin や phpMyAdmin みたいな感じのユーティリティソフトです。