Help us understand the problem. What is going on with this article?

なぜNSStringをプロパティにする際にcopyオプションを選択すべきなのか

More than 5 years have passed since last update.

さて、NSStringをプロパティとして保持すること、たくさんありますよね。Googleのコーディング規約にもcopyにするよう書いてあります。

自分もそうしていたのですが、なぜそうしなければいけないのかというところまで考えないエンジニア失格状態だったのでちゃんと考えてみました。

さて、そもそもcopyメソッドとは何なのか?

copyメソッド

copyメソッドはNSObjectで定義されているのですが、copyメソッドを呼ぶとcopyWithZone:メソッドが内部的に呼ばれるようになっています。

APIリファレンスを見るとしっかりと書かれています。

Return Value
The object returned by the NSCopying protocol method copyWithZone:,.

copyWithZone:メソッドはNSCopyingプロトコルのメソッドです。ただしNSObjectcopyメソッドの定義はしていますがNSCopyingプロトコルは採用していません。copyWithZone:が実装されていない場合には例外が発生します。

copyWithZone:メソッド

前述の通りNSCopyingプロトコルのメソッドです。

- (id)copyWithZone:(NSZone *)zone

引数はNSZone型のオブジェクトです。ゾーンは以前、実行効率を向上されるために使用されていたそうですが、もう使われていないとのこと(ゾーンについてはこちらを参照)。現在はcopyメソッドが呼ばれた際は引数にNULLを指定して自身のcopyWithZone:を呼び出すようになっています。

copyWithZone:についてAPIリファレンスを見てみると、このようなことが書かれています。

The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object; otherwise the exact nature of the copy is determined by the class.

immutableなオブジェクトを返すようになっています。ただcopyの振る舞いはクラスによって変わってくるようです。

プロパティのcopyについて

copyがクラスによって振る舞いを変えるということが分かりました。

ここでプロパティのオプションにcopyを指定すると、内部的にどのような処理が行われるか見てみましょう。

- (void)setName:(TYPE)obj {
    if (name != obj) {
        name = [obj copy];
    }
}

copyを指定した場合、このようにセッタが作られます。copyWithZone:を考慮すると、mutableなオブジェクトがセットされるとimmutableなオブジェクトになります。

NSStringのcopyメソッド

ここでようやくNSStringに触れられます。NSStringクラスのインスタンスにcopyを送っても同じアドレスを指すようになっています。

NSString *string = NSStringFromClass([self class]);
NSLog(@"%p",string); // 0x8dbb950
NSLog(@"%p",[string copy]); // 0x8dbb950

そもそもimmutable = 不変なオブジェクトで変わることは考えられないので、わざわざコピーを作る必要はないということなのでしょう。つまりimmutableなオブジェクトが渡された場合、(内部的にcopyは呼ばれるが)結果的にstrongと同じ動作になります。

NSMutableStringの場合は、通常のコピーが行われます。

NSMutableStringによる意図しない変更

ではプロパティのオプションをstrongにした場合に何が困るのでしょうか?

プロパティのオプションをstrongにした場合、参照しているNSMutableStringオブジェクトの値が変更されると、プロパティの値も変更されてしまい、意図しない変更が起きてしまいます。

それぞれプロパティを用意して試してみましょう。

ViewController.m
@interface ViewController ()
@property (nonatomic, strong) NSString *strongString;
@property (nonatomic, copy)   NSString *copiedString;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSMutableString *mutableString = [[NSMutableString alloc] initWithString:@"one, two, three"];

    self.strongString = mutableString;
    self.copiedString = mutableString;

    [mutableString appendString:@", four"];

    NSLog(@"%@",self.strongString);
    // one, two, three, four

    NSLog(@"%@",self.copiedString);
    // one, two, three
}

上記の例ではself.strongStringmutableStringと同じアドレスを指しているので、mutableStringが変更されたことによりself.strongStringも変更されてしまっています。

self.copiedStringの方は、copyが行われ新たなメモリ領域が作られます。よってmutableStringの変更の影響は受けません。

プロパティにcopyオプションを指定しておけば、NSMutableStringの代入を行って意図しない変更が発生する心配をせずに済みますね。

まとめ

以上、ここまでNSStringについて書きましたが、immutableなクラスのcopyがインスタンス自身を返すようになっていたり、mutableなクラスのcopyの返り値がimmutableなのはNSArrayNSDictionaryも同様です。

まとめるとこんな感じになります。

1.NSStringNSArrayなどのimmutableなクラスのcopyメソッドは同じアドレスを指すようになっている。よってstrongでもcopyでも動作は変わらない。

2.NSMutableStringcopyメソッドは、immutableなオブジェクトが返される。

mutableなクラスを渡されないことは保証できない、immutableなオブジェクトの場合はプロパティにcopyを指定してもstrongと変わらない動作をする。

これがcopyオプションを選択すべき理由です。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away