Kikuchy's Second Memory

技術のこととか、技術以外のこととか、思ったことを書き留めています。

Objective-C で、読み取り専用になるメンバを用意したい

http://www.flickr.com/photos/36352331@N08/3962410821
photo by mh.xbhd.org


データを管理していて、外部からは変更不可能なメンバ変数(プロパティ)を用意したいと思ったことがあると思います。
通常は、データを内部に隠蔽して、getterを通して外部に公開するというアプローチをとりますね。

Objective-Cではどのようにして行うのか、調べました。

想定読者


例えば、書籍を表現するクラスがあったとします。
そこら辺に置いておいた本のタイトルなどはコロコロ変わってもらっては困るので、本が作られたときのタイトルから変化しない物とします。
すると、タイトルなどは読み取り専用である必要があります。

C#では

C#でしたら、以下のように実装します。
getter 構文を使うと綺麗に実装できます。

class Book
{
	// 内部に隠しておくための変数
	private String _title;
	private String _author;
	
	public String Title
	{
		get { return this._title; }
	}
	
	public String Author
	{
		get { return this._author; }
	}
	
	// コンストラクタでデータをセット
	public Book(String title, String author)
	{
		this._title = title;
		this._author = author;
	}
}

このように定義する事で、外部からは Book.TitleBook.Author は読み取り専用になります。

Book aBook = new Book("アジャイルサムライ", "Jonathan Rasmusson");

// 読み取りは可能
String title = aBook.Title;

// 書き込みは不可能(コンパイルエラーになる)
aBook.Title = "アート・オブ・プロジェクトマネジメント";

Objective-Cでは

.h ファイルと .m ファイルにファイルを分けて実現します。

まずは、外部に公開する定義ファイル Book.h

@interface Book

// .hファイルの方では、属性に readonly を付ける
@property (strong, readonly) NSString *title;
@property (strong, readonly) NSString *author;

// イニシャライザもついでに公開しておく
-(instancetype) initWithTitle: (NSString *)title author: (NSString *)author;

@end


次は実装ファイル Book.m

// ()が必要
@interface Book ()

// .mファイルの方では、属性に readwrite を付ける
@property (strong, readwrite) NSString *title;
@property (strong, readwrite) NSString *author;

@end

@implementation Book

-(instancetype) initWithTitle: (NSString *)title author: (NSString *)author
{
	self = [[super alloc] init];
	if(self != nil)
	{
		self.title = title;
		self.author = author;
	}
	return self;
}

@end

これで外部からは読み取り専用のメンバを作ることができました。
この Book クラスを使用するコードでは Book.h をimportして使います。

Book *aBook = [[Book alloc] initWithTitle:@"アジャイルサムライ" author:@"Jonathan Rasmusson"];

// 読み取りは可能
NSString *title = aBook.title;

// 書き込みは出来ない
aBook.title = @"アート・オブ・プロジェクトマネジメント";