[Obj-C] 04 Objective-C 2.0学習 “オブジェクトの型と動的結合” 04-01

[Mac M2 Pro 12CPU, Ventura 13.6, clang 15.0.0]
教本:『詳解 Objective-C 2.0 第3版』(2011年) chap.04-01

動的バインディングとポリモーフィズムについて学びました。

コードの簡素化に寄与する反面、実行速度が遅くなるようです。

プログラミングをミスった場合、静的バインディングであればコンパイルエラーになり、動的バインディングであれば実行時エラーになります。

これは実務レベルで体感してみないとメリット・デメリットを深く理解できないように思いました。

取りあえず先に進みます。

#import <Foundation/NSObject.h>
#import <stdio.h>

@interface A : NSObject
- (void)whoAreYou;
@end

@implementation A
- (void)whoAreYou { printf("I'm A\n"); }
@end

@interface B : NSObject
- (void)whoAreYou;
@end

@implementation B
- (void)whoAreYou { printf("I'm B\n"); }
@end

int main(void)
{
    id obj;
    int n;

    scanf("%d", &n);
    switch (n) {
    case 0:  obj = [[A alloc] init]; break;
    case 1:  obj = [[B alloc] init]; break;
    case 2:  obj = [[NSObject alloc] init]; break;
    }
    [obj whoAreYou];
    return 0;
}
1
I'm B
2
2023-10-16 10:28:08.551 dyna[12438:464557] -[NSObject whoAreYou]: unrecognized selector sent to instance 0x600002a70000
2023-10-16 10:28:08.552 dyna[12438:464557] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject whoAreYou]: unrecognized selector sent to instance 0x600002a70000'
*** First throw call stack:
(
	0   CoreFoundation                      0x00000001aac0b104 __exceptionPreprocess + 176
	1   libobjc.A.dylib                     0x00000001aa729fd0 objc_exception_throw + 60
	2   CoreFoundation                      0x00000001aacb210c -[NSObject(NSObject) __retain_OA] + 0
	3   CoreFoundation                      0x00000001aab73030 ___forwarding___ + 1600
	4   CoreFoundation                      0x00000001aab72930 _CF_forwarding_prep_0 + 96
	5   dyna                                0x00000001026efefc main + 176
	6   dyld                                0x00000001aa75bf28 start + 2236
)
libc++abi: terminating due to uncaught exception of type NSException
Abort trap: 6

[Obj-C] 03 Objective-C 2.0学習 “継承とクラス”

[Mac M2 Pro 12CPU, Ventura 13.6, clang 15.0.0]
教本:『詳解 Objective-C 2.0 第3版』(2011年) chap.3

Objective-Cのクラス継承について学びました。

下記のコードでは[y method2]のところで循環するのではないかと思いましたが、superを呼び出した時点でスーパークラスはclass Aに固定されるようです。

今さらですが、@interfaceはヘッダファイル、@implementationはソースファイルに分割可能でしたね。Objective-Cアプリは昨年6月に初めて書いたきりなので、すっかり忘れていました。

#import <Foundation/NSObject.h>
#import <stdio.h>

@interface A: NSObject
- (void)method1;
- (void)method2;
@end

@implementation A
- (void)method1 { printf("method1 of Class A\n"); }
- (void)method2 { printf("method2 of Class A\n"); }
@end

@interface B: A
- (void)method2;
@end

@implementation B
- (void)method2 {
    printf("method2 of Class B\n");
    printf("self --> ");
    [self method1];
    printf("super--> ");
    [super method2];
}
@end

@interface C: B
- (void)method1;
@end

@implementation C
- (void)method1 { printf("method1 of Class C\n"); }
@end

int main(void)
{
    id x = [[B alloc] init];
    id y = [[C alloc] init];
    printf("--- instance of B ---\n");
    [x method1];
    [x method2];
    printf("--- instance of C ---\n");
    [y method1];
    [y method2];
    return 0;
}
--- instance of B ---
method1 of Class A
method2 of Class B
self --> method1 of Class A
super--> method2 of Class A
--- instance of C ---
method1 of Class C
method2 of Class B
self --> method1 of Class C
super--> method2 of Class A // 循環にならない

[Obj-C] 02 Objective-C 2.0学習 “Objective-Cのプログラム”

[Mac M2 Pro 12CPU, Ventura 13.6, clang 15.0.0]

しばらくObjective-CやSwiftを集中的に学んでいきます。教本は『詳解 Objective-C 2.0 第3版』(2011年)です。

CHAPTER02まで読みました。サンプルコードはXcodeではなくmakefileでコンパイルします。

メソッドの第1引数に変数名がない、そもそも引数そのものを変数名にしていない、というルールにVisual Basicに対する違和感と同様のものを覚えます。単に仕様が古いということでしょうか。

まあ細かいことは気にせず淡々と読み進めます。

#import "Volume.h"

@implementation Volume
- (id)initWithMin:(int)a max:(int)b step:(int)s
{
    self = [super init];
    if (self != nil) {
        val = min = a;
        max = b;
        step = s;
    }
    return self;
}

- (int)value
{
    return val;
}

- (id)up
{
    if ((val += step) > max)
        val = max;
    return self;
}

- (id)down
{
    if ((val -= step) < min)
        val = min;
    return self;
}
@end

[Obj-C] 01 UIKit iOS 3のコードを動かす

[Mac M2 Pro 12CPU, Ventura 13.6, iOS 17, Xcode 15.0]

昨年22年6月に気まぐれで購入したUIKitの本を通読していきます。『iPhoneプログラミングUIKit詳解リファレンス』(2010年)という本です。300円で買えました。

まずは出版社サイトにあるサンプルコードを以下の手順で動くようにしました。

1.プロジェクトファイル内にあるpbxprojファイルをエディタで開き、iphoneos3.1の3.1を削除するか最近のiOSバージョン番号にする。

2.Interface Builderの右にあるiOSバージョン番号設定を1と同じにする。

3.AppDelegateファイルに以下のように追記する。

#import "HelloWorldAppDelegate.h"

@implementation HelloWorldAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {    

    // Override point for customization after application launch
    window.rootViewController = [UIViewController new]; // この行を追加
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

[iOS] メモアプリ比較 2023年10月 Drafts, Bear

[iOS 17.0.3、watchOS 10.0.1]

開発中のメモアプリについてwatchOS版を改良するため、類似アプリの機能を調べています。

watchOS版では文字列を追加すると改行が消えてしまう仕様をなんとかしたいと考えています。watchOSでは複数行を扱えるTextEditorは使えず、単行用のTextFieldしか使えないためです。

類似アプリであるDraftsやBearでの実装を確認すると追加する文字列を1行として扱い、元のメモに結合させる方法で対処していました。

検証して分かったのですがDrafts、Bear共にwatchOS版はiCloudにあるデータと直接やり取りできないようです。iOS版を介してデータを取り込んでいます。その点は自製アプリの方が優れています。

私自身はiPhone、iPad、Macでメモを作成し、Apple Watchで内容を確認するという使用スタイルですから、iPhoneで一旦起動しないと最新にならないDraftsやBearは条件を満たしていないことになります。

あと起動すると同時にメモの内容が表示されるアクセス性の良さも自製アプリのウリでしょうか。DraftsのwatchOS版は起動して2タップし内容をようやく確認できます。Inboxを呼び出すコンプリケーションでは起動プラス1タップになり少しマシになります。

今のところApp Store公開は考えていませんが、出来栄え次第では2,3ドルで販売しようかな。

[C言語] Cパズルブック 2.3 その他の型変換

『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]

問2.3のコードと出力は以下の通りです。

ここで一旦Cパズルブックを使ったプログラミング学習を休止して、アプリ製作に戻ります。

#include "stdio.h"

#define PR(x) printf("x = %g\t", (double)x)
#define NL putchar('\n')
#define PRINT1(x1) PR(x1); NL
#define PRINT2(x1, x2) PR(x1); PRINT1(x2)

int main() {
    double d = 3.2, x;
    int i = 2, y;

    x = (y = d/i)*2; 
    PRINT2(x, y);
    // x = (y = (d/i))*2
    // x = (y = (int)1) *2
    // x = 1*2

    y = (x = d/i)*2; 
    PRINT2(x, y);
    // y = (x = (d/i))*2
    // y = (x = (double)1.6 *2
    // y = 3


    y = d * (x = 2.5/d);
    PRINT1(y);
    // y = d * (x = (2.5/3.2))
    // y = 3.2 * 2.5/3.2
    // y = 2

    x = d * (y = ((int)2.9 + 1.1)/d);
    PRINT2(x, y);
    // x = d * (y = ((int)2 + 1.1)/d);
    // x = d * (y = 3.1/3.2);
    // x = d * (y = 0);

    return 0;
}
x = 2	x = 1	
x = 1.6	x = 3	
x = 2	
x = 0	x = 0	

[C言語] Cパズルブック 2.2.4-6 整数と浮動小数点数の型変換

『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]

問2.2.4-6のコードと出力は以下の通りです。

特記すべきことはありません。

少しダレてきたので、他の教本に一旦移る予定です。Cパズルブックは第1章、第6章、第2章を順に完遂する形となり、キリとしては特に問題ないでしょう。何らかのタイミングでまた戻ってきます。

#include "stdio.h"

#define PR(x) printf("x = %.8g\t", (double)x)
#define NL putchar('\n')
#define PRINT4(x1, x2, x3, x4) PR(x1); PR(x2); PR(x3); PR(x4); NL

int main() {
    double d;
    float f;
    long l;
    int i;

    // 問2.2.4
    d = f = l = i = (double)100/3;
    // (d = (f = (l = (i = (double)100/3))))
    // (d = (f = (l = ((integer)100/3)))) and i=33
    // (d = (f = (long)33)) and l=33
    // (d = (float)33) and f=33
    // ((double)33) and d=33
    PRINT4(i, l, f, d);

    // 問2.2.5
    i = l = f = d = (double)(100000/3);
    // (i = (l = (f = (d = (33333)))))
    // (i = (l = (f = (double)33333))) and d=33333
    // (i = (l = (float)33333)) and f=33333
    // (i = (long)33) and l=33333
    // ((integer)33) and i=33333
    PRINT4(i, l, f, d);

    // 問2.2.6
    d = f = l = i = 100000/3;
    // (d = (f = (l = (i = 100000/3))))
    // (d = (f = (l = ((integer)33333)))) and i=33333
    // (d = (f = (long)33)) and l=33333
    // (d = (float)33) and f=33333
    // ((double)33) and d=33333
    PRINT4(i, l, f, d);

    return 0;
}
x = 33	x = 33	x = 33	x = 33	
x = 33333	x = 33333	x = 33333	x = 33333	
x = 33333	x = 33333	x = 33333	x = 33333

[C言語] Cパズルブック 2.2.2-3 整数と浮動小数点数の型変換 IEEE754

『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]

問2.2.2-3のコードと出力は以下の通りです。

doubleからfloatへの変換で最終桁が3から2に変わっていますが、これは64ビットから32ビットへの精度低下によるものです。

各変数がメモリにどう格納されているのかLLDBデバッガで確認しました。doubleとfloatはIEEE754内部表現に変換されて格納されています。ちなみにIEEEは米国電気電子学会を指します。

#include "stdio.h"

#define PR(x) printf("x = %.8g\t", (double)x)
#define NL putchar('\n')
#define PRINT4(x1, x2, x3, x4) PR(x1); PR(x2); PR(x3); PR(x4); NL

int main() {
    double d;
    float f;
    long l;
    int i;

    d = f = l = i = 100/3;
    // (d = (f = (l = (i = (100/3)))))
    // (d = (f = (l = (i = 33))))
    // (d = (f = (l = (integer)33))) and i=33
    // (d = (f = (long)33)) and l=33
    // (d = (float)33) and f=33
    // ((double)33) and d=33
    PRINT4(i, l, f, d);

    l = i = f = d = 100/3.;
    // (l = (i = (f = (d = (100/3)))))
    // (l = (i = (f = (d = 33))))
    // (l = (i = (f = (double)33))) and d=33.333333
    // (l = (i = (float)33)) and f=33.333332
    // (l = (integer)33) and i=33
    // ((long)33) and l=33
    PRINT4(i, l, f, d);

    return 0;
}
x = 33	x = 33	x = 33	x = 33	
x = 33	x = 33	x = 33.333332	x = 33.333333	

double d : 4040aaaaaaaaaaab (IEEE754内部表現) = 33.333333
float f : 42055555 (IEEE754内部表現) = 33.333332
int i : 21 (16進数) = 33
long l : 21 (16進数) = 33
※格納方式はリトルエンディアンのため右から読む

[C言語] Cパズルブック 2.2.1 整数と浮動小数点数の型変換 除算演算子 “/”

『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]

問2.2.1のコードと出力は以下の通りです。

整数同士の割り算では計算結果が整数(商)になる、というルールについてC++20規格での記載箇所をアップしておきます。たまに扱う度に引っかかるところなので規格上の根拠を確認しておきました。

要は(a/b)*b + a%b=aになるよう言語が設計されているということを意味します。商と剰余で元の整数を算出できます。

#include "stdio.h"

#define PR(x) printf("x = %.8g\t", (double)x)
#define NL putchar('\n')
#define PRINT4(x1, x2, x3, x4) PR(x1); PR(x2); PR(x3); PR(x4); NL

int main() {
    double d;
    int d2;
    float f;
    long l;
    int i;

    d = 100/3;
    d2 = 100%3;
    printf("d = %.8g\n",d);
    printf("d2 = %d\n",d2);

    int a;
    int b;
    double c;
    a = 100;
    b = 3;
    c = (a/b)*b + a%b;
    printf("c = %.8g\n",c);

    i = l = f = d = 100/3;
    // (i = (l = (f = (d = (100/3)))))
    // (i = (l = (f = (d = 33))))
    // (i = (l = (f = (double)33))) and d=33
    // (i = (l = (float)33)) and f=33
    // (i = (long)33) and l=33
    // ((integer)33) and i=33
    PRINT4(i, l, f, d);

    return 0;
}
d = 33
d2 = 1
c = 100
x = 33	x = 33	x = 33	x = 33	
除算に関する記述(C++20規格)

[C言語] Cパズルブック 2.1.2 文字型, 文字列型, 整数型 シフト演算子

『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]

問2.1.2のコードと出力は以下の通りです。

#include "stdio.h"
#define PRINT(format, x) printf("x = %"#format"\n",x)

int main() {
    int sx = -8;
    unsigned ux = -8;

    PRINT(o, sx);
    PRINT(o, ux);

    PRINT(o, sx >> 3);
    PRINT(o, ux >> 3);

    PRINT(d, sx >> 3);
    PRINT(d, ux >> 3);

    return 0;
}
x = 37777777770
x = 37777777770
x = 37777777777
x = 3777777777
x = -1
x = 536870911
-8 = 11111111111111111111111111111000
8進数:37777777770

3ビット右シフト
符号あり
11111111111111111111111111111111 右算術シフト(空白を符号ビット1で埋める)
8進数:37777777777(11桁)
10進数:-1

符号なし
00011111111111111111111111111111 論理シフト(空白を0で埋める)
8進数:3777777777(10桁)
10進数:536870911