1

私はプロパティを持っています:

@property(nonatomic, assign)UIView *currentView;

次のコードを処理すると、なぜ壊れるのですか?

_currentView  =nil;
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
_currentView = v1;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"_currentView %@", _currentView);     ///break here. 
NSLog(@"v1 %@", v1);

_currentViewとのv1両方が同じメモリを指していると思います。v1オブジェクトを実現するために使用し、オブジェクト_currentViewを印刷するために使用すると、クラッシュします。これは理解できます。

v1ただし、リリース後および印刷前に次の行を追加する場合_currentView。ログがわかりません。

v1 = nil;

次のようなコード

_currentView  =nil;
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
_currentView = v1;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
v1 = nil;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);

印刷結果は次のとおりです。

> 2012-05-30 15:16:57.314 All[3068:15203] _currentView <UIView:
0x81ccbc0; frame = (0 0; 0 0); layer = <CALayer: 0xa07e5a0>>
> 2012-05-30 15:16:57.798 All[3068:15203] v1 <UIView: 0x81ccbc0; frame =
(0 0; 0 0); layer = <CALayer: 0xa07e5a0>
> 2012-05-30 15:16:59.189 All[3068:15203] _currentView <UIView: 0x81ccbc0; frame = (0 0; 0 0); transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)
> 2012-05-30 15:17:09.042 All[3068:15203] v1 (null)

v1release と logを呼び出した後、出力_currentViewされるのはなぜですか

_currentView &lt;UIView: 0x81ccbc0; frame = (0 0; 0 0);
 transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)&gt;
4

5 に答える 5

2

@propertyアクセサを使用していないため、これは必ずしも属性(割り当てまたは保持)に関連しているとは限りません。

これはあなたのコードで起こることです:

@property(nonatomic, assign)UIView *currentView;

またはassignを使用していないため、この場合は関係ありませんが、ivarを宣言します。self.currentView[self setCurrentView:...];

_currentView = nil;
// You just made your pointer _currentView point to nil or 0

UIView *v1 = [[UIView alloc] initWithFrame:CGRectZero];
// You created an UIView object and made v1 to point to it. (v1 is NOT the real object)

_currentView = v1;
// You just made _currentView to point the same object v1 points to

NSLog(@"_currentView %@", _currentView);
// and because of that you correctly see the object here (because _currentView points to the view object)

NSLog(@"v1 %@", v1);
// also here (because v1 points to the object from the start)

[v1 release];
// now you release the object pointed by v1 , since no other is retaining it, it gets deallocated BUT note that v1 is still pointing to it, which now is garbage memory!)

//NSLog(@"_currentView %@ v1 %@", _currentView, v1);
// If above line were executed the app will crash because of v1 and _currentView both are pointing to the object that was just released and it is not valid anylonger.

v1 = nil;
// Now you made v1 to point to nothing so next time you use it terrible things will not happen (★) :)

NSLog(@"_currentView %@", _currentView);
// Oh no! you called _currentView and since it was still pointing to the object you released a bit ago the app crashes :(

NSLog(@"v1 %@", v1);
// This is fine, you set v1 to point to nil so it is not pointing to some garbage memory you simply get nil.

(★)Objective-cではメソッドの送信nilは無害であるため、他のメソッドのパラメーターとしてnilを使用することは別の話です

別物:

適切に割り当てとして宣言されているため、結果self.currentView = v1;の代わりに書き込む場合でも同じになります。_currentView = v1;

プロパティをとして宣言すると、状況が異なりますretain。その場合[v1 release];、オブジェクトはcurrentView()によって保持されているため、実行した後、オブジェクトの割り当ては解除されませんself.currentView = v1。そうすると、v1 = nilv1はnilを指し、オブジェクトはcurrentViewによってのみ到達可能になります。次に、そうすると_currentView = nil、_currentViewはnilを指しますが、アクセサリメソッドを使用しなかった(または明示的に解放されなかった)ため、オブジェクト自体は解放されないため、ダングリングポインタが取得されます。

保持として宣言されたプロパティがすべて解決策であるとは限りません。ケースバイケースです。Obj-cのメモリ管理についてもう少し(少なくともこれは)Cポインターについて、次にARCについてもう少し読むことをお勧めします。

于 2012-05-30T08:21:23.817 に答える
1

2 番目の出力で異なる印刷結果が得られる理由は次のとおりです。

実行後: v1_currentView[v1 release];の両方が古いメモリ ブロックを指しています。ただし、設定はv1のみを nill に設定し、_currentView は設定しませ(これらはポインターであることを思い出してください)。v1 = nil;

これがあなたにとって物事を明確にすることを願っています。

敬具、
ボー

于 2012-05-30T07:37:38.633 に答える
0

nacho4dの答えに1つのポイントを追加したいだけです。NSLog割り当てを解除したオブジェクトの場合、クラッシュすることもあれば、クラッシュしないこともあります。オブジェクトの割り当てが解除されると、空きメモリブロックのリストにオブジェクトが追加されます。メモリの実際のコンテンツは依然としてオブジェクトのように見え、ブロックの一部またはすべてが再利用されるまで、メッセージの送信が機能することがよくあります。

割り当て解除されたオブジェクトをNSLogすると、次の3つのうちの1つが発生する可能性があります。

  • まだ生きているかのようにログに記録できます
  • まったく同じ場所で開始した場合、まったく異なるオブジェクトが応答する可能性があります
  • EXC_BAD_ACCESSを取得します

どちらが起こっても、主に偶然の問題です。

于 2012-05-30T10:27:28.980 に答える
0

問題は、プロパティをどのように宣言したかです。

@property(nonatomic, assign)UIView *currentView;

そのはず:

@property(nonatomic, retain)UIView *currentView;

あなたがしようとするとNSLog、あなたが以前にそれを解放したので、それはゴミの勇気を持っています:

[v1 release];
NSLog(@"_currentView %@", _currentView);

この時点で、これを実行しようとするNSLogと、v1_currentViewが同じメモリブロックを指していることに注意してください。

于 2012-05-30T07:31:02.783 に答える
0

プロパティを宣言していますが、それを使用していません。インスタンス変数を直接使用しています。また、変数が指しているメモリを保持できません。

クラスで宣言されたインスタンス変数があるようです:

@interface MyClass : NSObject {
    UIView * _currentView;
}

@end

あなたがしていることは、プロパティを使用せずにこれに直接アクセスしていることです。何が起こるかというと、割り当てるときにメモリを保持していないということです。つまり、メモリを完全に解放していて、メモリが削除されています。このように動作させるには、次のようにします。

UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
[_currentView release];
_currentView = [v1 retain]; // <-- OBSERVE the retain
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"_currentView %@", _currentView);     // Should not break anymore
NSLog(@"v1 %@", v1);

後で、_currentView に保持されているオブジェクトを解放する必要があります。

-(void)dealloc {
    [_currentView release];   
}

(_currentView に新しい値を割り当てたい場合は、これも行う必要があることに注意してください)

別の方法は、宣言したプロパティを実際に使用することですが、代わりに保持プロパティを使用します。

@property(nonatomic, retain)UIView *currentView;

アクセス可能にするには、クラスの実装で合成する必要があります。

@implementation MyClass 

@synthesize currentView = _currentView;

/*...*/

@end

これにより、保持が処理されます。また、変数に格納されている以前の値を解放することについて考える必要はありません。これは処理されるためです。ただし、次の方法でプロパティにアクセスする必要があります。

   self.currentView

コード例は次のようになります。

UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
self.currentView = v1;
NSLog(@"currentView %@", self.currentView);
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"currentView %@", self.currentView);
NSLog(@"_currentView %@", _currentView);     ///Should not break anymore 
NSLog(@"v1 %@", v1);

プリントアウトからわかるように、2 つの変数は依然として同じメモリを指しています。違いは、メモリを保持しているため、メモリに保持カウンターが追加されることです。保持カウンタが 0 になるまでメモリは解放されません。つまり、保持するたびに 1 回解放する必要があります。また、後のケースでは、dealloc メソッドで保持を解放する必要があります。

-(void)dealloc {
    [_currentView release];   
}

最後の質問はこの列

v1 = nil;

v1 が指すアドレスにのみ影響します。変数 _currentView にも、それが指しているメモリにも影響しません。

于 2012-05-30T11:17:58.823 に答える