7

はNSObjectの方法を使用し-(id)awakeAfterUsingCoder:(NSCoder *)decoder例として、ドキュメントは言う:自動参照カウントで自己を解放するための新しいパターンは何ですか?

は、自身のために別のオブジェクト を置換する、デコードされた後に、オブジェクトを許可します。例えば、フォントを表すオブジェクトは、 がデコードされると、それ自体を解放して、 と同じフォント記述を持つ既存のオブジェクトを返すことがあります。このようにして、冗長オブジェクトは削除することができます。

通常あなたはこの行を残しているARCで

[self release]; 
return substitutedObject; 

を行うだろう。これは漏れませんか?または、私は元のオブジェクトをリリースするためにNSCoderオブジェクトを信頼する必要がありますか?もしそうなら、最初に非ARCコードを使って自明にリリースしなければならないのはなぜですか?

私はself = nilは、コンパイラーのドキュメントは、自己についての言うことに照らして正しいとは思わない:http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.self

答えて

2

前述のように、[self release];と書くことはできません。さらに、awakeAfterUsingCoder:で、初期化ツールではありません - selfを再割り当てすることはできません。

このリークはありませんか?

はい。以下のプログラムで証明されています。

NSCoderオブジェクトを信頼して元のオブジェクトを解放するだけでいいですか?漏れを避けるために

一つのアプローチは、以下の存在 - 私が頭に浮かんだだけで最初のアプローチ、「新しいパターン」それを呼び出すことはありません。それはselfの明示的な解放を含み、この場合には、明示的なは、結果の保持:同様の問題は、Mac OS X上NIBトップレベルのオブジェクトのコンテキストで発生し

#import <Foundation/Foundation.h> 

@interface MONBoolean : NSObject <NSCoding> 

- (id)initWithBool:(bool)pBool; 

- (bool)isTrue; 
- (bool)isFalse; 

@end 

static NSString * const MONBoolean_KEY_value = @"MONBoolean_KEY_value"; 

@implementation MONBoolean 
{ 
    bool value; 
} 

- (id)initWithBool:(bool)pBool 
{ 
    self = [super init]; 
    if (0 != self) { 
     value = pBool; 
    } 
    return self; 
} 

- (bool)isTrue 
{ 
    return true == value; 
} 

- (bool)isFalse 
{ 
    return false == value; 
} 

- (NSString *)description 
{ 
    return [[NSString alloc] initWithFormat:@"<%s:%p> : %s", object_getClassName(self), self, self.isTrue ? "true" : "false"]; 
} 

- (void)encodeWithCoder:(NSCoder *)aCoder 
{ 
    [aCoder encodeBool:value forKey:MONBoolean_KEY_value]; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super init]; 
    if (0 != self) { 
     value = [aDecoder decodeBoolForKey:MONBoolean_KEY_value]; 
    } 
    return self; 
} 

- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder 
{ 
    const bool b = value; 
    // cannot reassign self outside of an initializer. 
    // if not released, will result in a leak: 
    CFRelease((__bridge const void*)self); 
    MONBoolean * result = [[MONBoolean alloc] initWithBool:b]; 
    // now we have to retain explicitly because this is 
    // an autoreleasing method: 
    CFRetain((__bridge const void*)result); 
    return result; 
} 

@end 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     MONBoolean * a = [[MONBoolean alloc] initWithBool:true]; 
     NSData * data = [NSKeyedArchiver archivedDataWithRootObject:a]; 
     MONBoolean * b = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 
     NSLog(@"%@", b); 
    } 
    system("leaks NAME_OF_PROCESS_HERE"); 
    return 0; 
} 
+0

結果を明示的に保持する必要がある理由を明確にすることはできますか?確かにそれはARCの目的を破っている。他のすべてにお答えいただきありがとうございます。 – jamesmoschou

+0

@moshyよろしくお願いします。はい - 私は最初、その細部を見落としました。その理由は 'awakeAfterUsingCoder:'は所有していない(または自動リリースされた)参照を返してしまうからです。そのため、ARCは私達の戻り値に対してref-count減少を挿入します。私たちがしたいことは、あるオブジェクトから別のオブジェクトへの参照を効果的に*転送することです。私は楽器でそれを走らせました - 漏れはありません。ゾンビはありません。明示的な保持なしでは、ゾンビがメッセージされるだろう。明示的なリリースなしに - リーク。自分自身で試してみてください( '@ autorelease'ブロックを' while(1) 'に入れてください)。 – justin

+1

メソッドを '__attribute __((objc_method_family(init)))'で 'init'ファミリのメンバにしてから' self'を再割り当てできますか? –

0

私はARCは、すべてのオブジェクトを追跡するために十分にスマートであると考えています。だからあなたはメモリについて何も言うことができず、アプリケーションはもはや使用されていないときにオブジェクトをリリースするはずです。場合によっては、漏れプロファイラを使用して実行しますが、大丈夫です。

+4

しかし私の理解では、自動参照カウントは依然として参照カウントです。ガベージコレクションと同じではありません。したがって、もはや使用されていないときに漏れているオブジェクトを持つことは(理論上)可能です。私はプロファイラを通してそれを実行します。 – jamesmoschou

4

Resource Programming Guideは言う:

ファイルの所有者は、NSWindowControllerNSViewControllerのインスタンスでない場合は、トップレベルの参照カウントを自分でオブジェクトをデクリメントする必要があります。手動参照カウントでは、最上位のオブジェクトにreleaseメッセージを送信することでこれを実現することができました。 ARCでこれを行うことはできません。代わりに、トップレベルのオブジェクトへの参照をCore Foundationタイプにキャストし、CFReleaseを使用します。

このような状況でも、この技術が使用されている可能性があります。 CFRelease((__bridge CFTypeRef)self);