2011-04-29 78 views
1

我正在研究內存管理,並且我做了一個管理表對象的小程序。NSMutableArray包含的對象和內存管理

這裏是我的.h:

@interface Person : NSObject { 
     NSString *firstName; 
     NSString *lastName; 
} 

@property (readwrite, copy) NSString *firstName; 
@property (readwrite, copy) NSString *lastName; 

這裏是我的.m:

- (IBAction)clic2:(id)sender { 
    Person *pers = [[Person alloc]init]; 
    NSMutableArray *myarray = [[NSMutableArray alloc] init]; 

    [pers setFirstName:@"FN 1"]; // = pers.firstName 
    [pers setLastName:@"LN 1"]; // = pers.lastName 
    [myarray addObject:pers]; 

    [pers setFirstName:@"FN 2"]; 
    [pers setLastName:@"LN 2"]; 
    [myarray addObject:pers]; 

    [pers release]; 


    for(int i = 0; i < [myarray count]; i++) 
     { 
     pers = [myarray objectAtIndex:i]; 
     NSLog(@"%d %@ %@ %@", [myarray objectAtIndex:i], pers, pers.firstName, pers.lastName); 
     } 

} 


- (void)dealloc 
{ 
    [firstName release]; 
    firstName = nil; 

    [lastName release]; 
    lastName = nil; 

    [super dealloc]; 
} 

,這是我的NSLog

 
2011-04-28 21:40:11.568 temp[4456:903] 4495296 <Person: 0x1004497c0> FN 2 LN 2 
2011-04-28 21:40:11.571 temp[4456:903] 4495296 <Person: 0x1004497c0> FN 2 LN 2 

正如你所看到的,所存儲的信息是一樣的;這似乎是因爲myarray總是存儲陣列中人員的相同內存地址/內容。

我明白,當我在第二次調用pers的內容時,系統也會更改myarray中的信息。

我該如何做以免覆蓋數據?

謝謝你的回答。


編輯:本例是2人,但這個想法是管理給出一個無限數量


EDIT2:謝謝一切都在這種情況下

回答

2

該數組不會複製添加到它的對象,它只是保留它們。對於-clic2:方法的範圍,您只有一個Person對象。您設置數據並將其添加到數組中。該數組存儲一個指向Person對象的指針並增加其保留計數。但是,您仍然只有一個Person對象,因此對首字母或姓氏的任何更改都會覆蓋您設置的原始數據。在-clic2:方法結尾的數組中有兩個對同一Person對象的引用。

你需要的是這樣的:

- (IBAction)buttonClicked: (id)sender 
{ 
    Person   *person; 
    NSMutableArray *array; 
    NSInteger   i, count = 10; 

    array = [[ NSMutableArray alloc ] initWithCapacity: count ]; 
    for (i = 1; i <= count; i++) { 
     /* create a new Person object on each iteration through the loop */ 
     person = [[ Person alloc ] init ]; 
     /* person has a -retainCount of at least +1 after allocation */ 

     person.firstName = [ NSString stringWithFormat: @"FN %d", i ]; 
     person.lastName = [ NSString stringWithFormat: @"LN %d", i ]; 

     /* adding the object increments the person retainCount to at least +2 */ 
     [ array addObject: person ]; 

     /* 
     * -release decrements the retainCount, so when we release the array 
     * at the end of the method and it sends release to all its objects 
     * the memory allocated for the Person objects will be reclaimed. 
     */ 
     [ person release ]; 
    } 

    for (person in array) { 
     NSLog(@"%@ %@ %@", person, person.firstName, person.lastName); 
    } 

    /* releasing the array also sends -release to all its objects */ 
    [ array release ]; 
} 
+0

謝謝,有一個問題,這是這樣做的常見做法? – Vyggo 2011-04-29 03:25:47

+0

當然。想象一下,如果你正在逐行閱讀包含名字和姓氏的文本文件。您仍然需要一個唯一的Person對象來存儲從每行文本讀取的數據。 – 2011-04-29 03:33:42

+0

工作正常。謝謝。 – Vyggo 2011-04-29 03:47:46

2

內存管理的解釋在您的代碼

[pers setFirstName:@"FN 1"]; // = pers.firstName 
[pers setLastName:@"LN 1"]; // = pers.lastName 
[myarray addObject:pers]; 

[pers setFirstName:@"FN 2"]; 
[pers setLastName:@"LN 2"]; 
[myarray addObject:pers]; 

您正在向您的數組添加相同的對象兩次,數組是stori ng指向你的Person對象的指針pers當你第二次爲內存中的同一對象做第二次setFirstName:setLastName時,你的指針是一樣的。當你調用addObject時:它只會增加該對象的保留計數並存儲它的指針,而不會創建它的副本。

創建名爲pers2的第二個Person對象並執行以下操作。

[pers1 setFirstName:@"FN 1"]; // = pers.firstName 
[pers1 setLastName:@"LN 1"]; // = pers.lastName 
[myarray addObject:pers1]; 

[pers2 setFirstName:@"FN 2"]; 
[pers2 setLastName:@"LN 2"]; 
[myarray addObject:pers2]; 

另外不要忘記當你完成時釋放myarray,目前你正在泄漏該對象。

另一個需要注意的是,你應該使用快速枚舉來迭代你的數組。

for(Person *person in myarray) { 
    // your code 
} 
3

您只創建一個人的實例並多次修改它(覆蓋進程中的值)。將一個對象添加到數組不會創建它的副本。它只維護對該數組中該對象的引用。

這裏是你的代碼可以做一個例子 -

NSMutableArray *myarray = [[NSMutableArray alloc] init]; 

Person *pers1 = [[Person alloc]init]; 
[pers1 setFirstName:@"FN 1"]; // = pers1.firstName 
[pers1 setLastName:@"LN 1"]; // = pers1.lastName 
[myarray addObject:pers1]; 
[pers1 release]; 

Person *pers2 = [[Person alloc]init]; 
[pers2 setFirstName:@"FN 2"]; 
[pers2 setLastName:@"LN 2"]; 
[myarray addObject:pers2]; 
[pers2 release]; 
2

這是發生,因爲你只能有一個Person對象。

當你這樣做:

Person *pers = [[Person alloc] init]; 

pers持有一個Person對象的內存塊;它包含一個位置,或者地址。這是一個「指向人的指針」。當你這樣做:

[myarray addObject:pers]; 

該陣列不復制任何內存塊;它所做的就是複製的位置,它由pers表示。*當您更改pers的變量值時,它會更改同一塊中的內存。您仍然在同一位置有Person對象,但內容不同。當您再次添加pers到數組:

[myarray addObject:pers]; // Second time 

數組只是使指針,仍然只具有相同的位置,所以數組指針的兩個副本的另一個副本;兩個地址是相同的。因此,當您查看這些地址處的值時,您會看到相同的值,因爲它實際上只是一個對象。


*這也表明,它感興趣的周圍保持該內存通過在位置調用對象的retain

1

是的,你說得對。 NSMutableArray將指針存儲到Person對象,而不是對象本身/副本。

如果你真的想要複製最好的東西,你可以做的是讓Person類實現NSCopying協議。

在.H添加NSCopying聲明,保留其餘相同

@interface Person : NSObject <NSCopying> { 
... 
} 

.M(和以前一樣,但是追加此方法)

- (id)copyWithZone:(NSZone *)zone { 
    Person *objectCopy = [[Person allocWithZone:zone] init]; 
    objectCopy.firstName = self.firstName; 
    objectCopy.lastName = self.lastName; 
    return [objectCopy autorelease]; 
} 

那麼你可以做:

Person *pers = [[Person alloc]init]; 
NSMutableArray *myarray = [[NSMutableArray alloc] init]; 

[pers setFirstName:@"FN 1"]; // = pers.firstName 
[pers setLastName:@"LN 1"]; // = pers.lastName 
[myarray addObject:[pers copy]]; 

[pers setFirstName:@"FN 2"]; 
[pers setLastName:@"LN 2"]; 
[myarray addObject:[pers copy]]; 

[pers release]; 
+0

感謝的,做工精細。一個問題:如果每次創建新指針時,爲什麼要使用allocWithZone而不是Alloc? – Vyggo 2011-04-29 03:50:26