Objective-C 文字列, initWithString, stringWithString 【初級編 第17回】

この記事は2013年1月4日に書かれたものです。内容が古い可能性がありますのでご注意ください。


Objective-C 文字列, initWithString, tringWithString 【初級編 第17回】

文字列の生成について

前回は@”文字列”を使い文字列定数として文字列を生成していましたが、文字列の生成はこれ以外にも方法があります。
今回は「stringWithString」と「initWithString」というメソッドを使用する方法を紹介します。
「stringWithString」メソッドと「initWithString」メソッドは以下のように定義されています。
+ (id) stringWithString: (NSString*)aString
– (id) initWithString: (NSString*)string

修飾子を見ていただくと「+」「-」とあるように「stringWithString」はクラスメソッドですのでインスタンス化せずに
使用できます。「initWithString」はインスタンス化しなければ使用できませんので使用するときは気を付けてください。
まずは「stringWithString」の方から使用法を見てみます。

stringWithStringメソッド

前回のコードを置き換えてみます。書き換えるのは「main.m」だけとなります。
main.m

#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import "Bmi.h"
#import "Person.h"
#import "Manage.h"

int main(void){
    NSString *name = [NSString stringWithString:@"Yamada"];
    Person *teacher = [[Person alloc] initWithWeight:56.0 height:1.72 name:name];
    printf("Teacher Name:%sn",[[teacher name] UTF8String]);

    Manage *manage = [[Manage alloc] init];
    [manage setPerson:teacher];
    [teacher release];
    [manage showBestPerson];
    [manage release];

    return 0;
}

実行してみると以下のようになりました。
1301_pic1

main関数の最初に以前出てきたNSAutoreleasePoolを作成することででなくなります。

#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import "Bmi.h"
#import "Person.h"
#import "Manage.h"

int main(void){
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *name = [NSString stringWithString:@"Yamada"];
    Person *teacher = [[Person alloc] initWithWeight:56.0 height:1.72 name:name];
    printf("Teacher Name:%sn",[[teacher name] UTF8String]);
    Manage *manage = [[Manage alloc] init];
    [manage setPerson:teacher];
    [teacher release];
    [manage showBestPerson];
    [manage release];
    [pool drain];

    return 0;
}

8行目で自動解放プールを作成しています。
17行目のところで自動解放プールを解放しています。以前の記事ではNSAutoreleasePoolの解放に「release」を使用していましたが、
それ以外にも「drain」というのもあります。
リファレンスによるとretainCountで管理している場合は、特に違いはないそうですが、ガーベージコレクション環境では
違いがあるようです。
「drain」はガーベージコレクションを開始させるものの「release」はガーベージコレクションを開始させません。
そこで今回はどちらでもいいように「drain」を使用します。
ちなみにIPhoneではガーベージコレクションは使用できません。

このときNSString型のポインタ変数であるnameですが、解放する必要はありません。
「stringWithString」の場合、戻り値はid型となっています。しかし実装のほうをみると作成時に自動的に自動解放プールに
オブジェクトを加えてからポインタを返すという処理になっていました。そのためreleaseの必要がないということです。

initWithStringメソッド

では次に「initWithString」の方を見てみます。
main.mを書き換えて見ます。「stringWithString」はコメントアウトして残しておきます。
main.m

#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import "Bmi.h"
#import "Person.h"
#import "Manage.h"

int main(void){
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *name = [[NSString alloc] initWithString:@"Yamada"];
    //NSString *name = [NSString stringWithString:@"Yamada"];
    Person *teacher = [[Person alloc] initWithWeight:56.0 height:1.72 name:name];
    printf("Teacher Name:%sn",[[teacher name] UTF8String]);
    Manage *manage = [[Manage alloc] init];
    [manage setPerson:teacher];
    [teacher release];
    [manage showBestPerson];
    [manage release];
    [pool drain];

    printf("retain%dn",[name retainCount]);
    [name release];

    return 0;
}

9行目でNSString型のポインタ変数nameを作成していますが、先ほどと違い「alloc」してから作成しています。
上で述べたようにインスタンスメソッドであるため、インスタンス化してからでないと「initWithString」メソッドは
使用できません。「alloc」してるということは「release」の必要もあるということで21行目で解放処理を入れています。
20行目は試しに自動解放プールを解放してからのretainCountを確認しています。結果には出していませんが
「stringWithString」ではセグメンテーション違反となります。

実行結果は以下のようになります。
1301_pic2
自動解放プールの解放ではnameは解放されてません。

このようにメソッドよって、作成したオブジェクトが自動解放される場合とされない場合があります。
これはほかのクラスなどにも当てはまることになります。
目安としては、「alloc」「copy」「copyWithZone」「new」などのメソッドやその単語が含まれるメソッドはretainCountを
1にし、所有者となるため解放処理が必要となります。

今回のまとめ

今回のまとめとしては
・文字列を作成するには「stringWithString」や「initWithString」といったメソッドを使用することができる
・使用するメソッドによってはオブジェクト作成時に自動解放プールへ登録してくれるものがある
・「alloc」や「copy」、「copyWithZone」、「new」などが含まれるメソッドの場合は、自動解放プールには登録されないので
手動で解放するなり登録するなりの処理が必要である
といったところでしょうか。

参考

参考までに私の環境でのそれぞれのメソッドの実装はこうなっていました。
まずは「stringWithString」からです。
NSString.mファイル内に以下のように実装されています。

+ (id) stringWithString: (NSString*)aString
{
  NSString  *obj;

  obj = [self allocWithZone: NSDefaultMallocZone()];
  obj = [obj initWithString: aString];
  return AUTORELEASE(obj);
}

AUTORELEASEというのはマクロで、自動解放プールにオブジェクトを
登録するという処理をします。内部ではinitWithStringメッセージを送ってますね。

NSAutoreleasePool.hファイルにはAUTORELEASEマクロに関して以下の記述がありました。
オブジェクトを自動解放プールに追加してautoreleaseメソッドを呼び出すとありますね。
~~~以下抜粋~~~
AUTORELEASE() macro to call the [NSObject-autorelease]method,
which adds an object to the current autorelease pool by
calling [NSAutoreleasePool+addObject:].
~~~ここまで~~~

「initWithString」も同様にNSString.mファイル内に実装されています。

- (id) initWithString: (NSString*)string
{
  unsigned  length = [string length];

  if (length > 0)
    {
      unichar   *s = NSZoneMalloc([self zone], sizeof(unichar)*length);

      [string getCharacters: s range: ((NSRange){0, length})];
      self = [self initWithCharactersNoCopy: s
                     length: length
                   freeWhenDone: YES];
    }
  else
    {
      self = [self initWithCharactersNoCopy: (unichar*)0
                     length: 0
                   freeWhenDone: NO];
    }
  return self;
}

AUTORELEASEマクロ使われていませんね。

  • このエントリーをはてなブックマークに追加

PAGE TOP