13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2022年にいまさら振り返るObjective-C

Last updated at Posted at 2022-12-22

好きな言語はC++です.
いかがお過ごしでしょうか.私は今朝寝違えたため,首がとても痛いです.

初めに

Objcを触る機会があって,基本ができていない気がしてきたので,今年の4月頃に勉強しました.
それをこの2022年の最後として,Objective-Cを再確認しよう!という記事です.

すべてを書くと長すぎるので端折るか,また違う記事として書こうと思います.

間違っているところがありましたら,ぜひコメントお願いします!

Objective-C

名前の通りオブジェクト指向の言語で,メッセージ式と呼ばれる記述方法が特徴的です.

// - (id) a; で自分自身を返すメソッド
[object a];
// ネストできる
[[[object a] a] a];

初めて見たときは, なんかLISPみたいな見た目してんな...と思いました.

コンパイラ指定子

@から始める命令のことで,代表的なのは@intarface,@implementation, @property,@synthesizeとかです.
Objcでは直接C言語を使用できるので,区別できるようになっています.

クラス

クラスを宣言するときには,実装ファイル (m/mm)インターフェイスファイル (h)の二つに分けて記述します.

インターフェース

ヘッダファイルには外部に公開したいメンバ関数(メソッド)などを記述します.C++でいうところのpublicに指定したものに相当します.ここで宣言したメソッドは必ず実装しなければなりません.また継承するにはクラス名の隣に: 親クラス名とします.

@interface ClassA: NSObject
@property (nonatomic) int a;

- (id) initWithValue: (int) value;
- (void) addAndSayA:(int) value;

@end

-から始まるものはインスタンスメソッドということを示しています.+から始まればクラスメソッドです.

id型はいわばvoid *型です.型が変わる動的に扱いたいときにはこのidが使えるわけですね.

実装

実装ファイルにはプライベートなメソッドとインターフェイスで宣言されたメソッドの両方を記述していきます.
self.*としてアクセスすることに注意です.この記述では気が付きませんが,実はこれゲッタとセッタを通してます.C/C++の感覚でいると???ですので... 

@implementation ClassA
@synthesize a;

- (id) initWithValue: (int) value{
    if(self = [super init]){
        self.a = value;
    }
    return self;
}

- (void) __addA:(int) value{
  self.a += value;
}

- (void) addAndSayA:(int) value{
  [self __addA: value];
  NSLog(@"%d", self.a);
}

@end

property

先のコードではGetter/Setterなどのアクセサを宣言していなかったと思います.実装ファイルでドット演算子を使用し,
自身のインスタンス変数にアクセスすることができました.本来であればドット演算子は使用できず,[]を使わなければなりません.またGetter/Setterの宣言・定義も必要なはずです.

これは@propertyというコンパイラ指定子を利用することで自動的にアクセサの生成 してくれるからだそうです.

重要なものとして,この指定子はインスタンス変数の生成を行います.つまり,プロパティを使用しなければ以下のように記述する必要があります.昔のObj-Cだと以下の様に記述するのが普通だったらしいですが,モダンObj-Cでは@propertyの使用が推奨されています.

@interface ClassA: NSObject{
   int a;
}
// 省略

この場合,self->aでアクセスします.アロー演算子でアクセスした場合には,生のメンバをさわることのになるので,自動生成されたゲッタとセッタを通しません.したがって,propertyにatomicを指定していても,排他処理などを通さないため,スレッドセーフでなくなります.
何らかの意図がない限りself.aでアクセスしましょう.

プロパティでは属性の設定が詳しく行えます

オプション名 説明
readonly 読み取りのみ
readwrite 読み書き可能
assign 代入によって後で設定されることを示す.プリミティブな型に指定
retain 保持されながら設定されることを示す.
strong 強参照
weak 弱参照
copy コピーされて設定されることを示す.強い参照だがコピーを受け取る.
getter ゲッターの名前を指定
setter セッターの名前を指定
atomic スレッドセーフ
nonatomic スレッドセーフにしない

相容れない設定でなければすべて設定することができます.
例えば

@property (nonatomic, strong) int a;

などです.

atomic / nonatomic

atomicを指定した場合, セッターにはlinuxでいうところのpthread_mutexで囲んで,ロックとアンロックをかけてくれます.

ゲッターには, セッターの排他制御に加え,retainautoreleaseが追加されます.これらはそれぞれ確保自動的な開放(参照が切れたら解放) を行います.つまり,ゲッターで取得されたオブジェクトや値が勝手に開放されるのを防いでくれます.

synthesize

こいつは@propertyで宣言されたインスタンス変数に対して,実装ファイル内で自動的に変数の生成を行ってインターフェースと統合してくれます.
@synthesize 宣言しないと_*という名前で自動的に生成されますが,Xcodeで警告がたくさん出るので書いた方がよさそうです.

ちなみに@synthesize のスコープはこのコンパイラ指定子より後ろなので,@implementaionの直後に書くのが良いです.

クラス(Object)をメンバ変数にするとき

クラスを使用するときはポインタで扱います.NSArrayならば

@property (atomic) NSArray* ary; 

となります.C++の感覚で行くと自分が作ったクラスをほかのクラスで利用するときヘッダファイルに#includeすると思います.Objc も同様で#importすればいいのですが,@classを利用するとクラスのプロトタイプ宣言的なことが行えます.そうすることで,余計な情報を読み込まなくて済みます.

「余計な情報」というのは例えば, Project-Swift.hのような自動変換されたコードで,恋ったコードをヘッダファイルに読み込んでしまうと,読み込んだ側のヘッダファイルの読み込み先まで影響が出てしまいます.

これの問題はC++のコードでも同じで, C/C++のヘッダが読み込まれるとそのファイルは純粋なObjective-Cと認識されなくなるため,他のファイルからimportした際にC/C++のコードとして認識されてしまいます.

従って,私はObjective-CのヘッダファイルではC/C++のヘッダファイルは読み込まず, プロトタイプ宣言に止めています.

P.S.
Objective-Cの情報や書き方のパターンがたくさんあってどれがいいのかとても悩みました.
書き方やスタイルについてはこちらのNYTimesのガイドが参考になりました.

13
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?