2009-03-04 26 views
8
//creates memory leak 
    self.editMyObject = [[MyObject alloc] init]; 

//does not create memory leak 
    MyObject *temp = [[MyObject alloc] init]; 
    self.editMyObject = temp; 
    [temp release]; 

Dòng đầu tiên của mã tạo ra rò rỉ bộ nhớ, ngay cả khi bạn thực hiện [self.editMyObject release] trong phương thức dealloc của lớp. self.editMyObject thuộc loại MyObject. Dòng thứ hai không bị rò rỉ bộ nhớ. Là dòng đầu tiên chỉ là không chính xác hoặc là có một cách để giải phóng bộ nhớ?Tại sao điều này tạo ra một rò rỉ bộ nhớ (iPhone)?

+0

Ba câu trả lời hay. Lưu ý chắc chắn ai cũng đưa ra câu trả lời. – 4thSpace

Trả lời

10

Hành vi chính xác phụ thuộc vào khai báo của editMyObject @property. Giả sử nó được delcared như

@property (retain) id editMyObject; //id may be replaced by a more specific type 

hoặc

@property (copy) id editMyObject; 

sau đó chuyển nhượng qua self.editMyObject = giữ hoặc bản sao các đối tượng được giao. Vì [[MyObject alloc] init] trả về một đối tượng được giữ lại, mà bạn là người gọi, bạn có thêm một lần giữ lại đối tượng MyObject và do đó nó sẽ bị rò rỉ trừ khi nó có một bản phát hành phù hợp (như trong khối thứ hai). Tôi sẽ đề nghị bạn đọc Memory Management Programming Guide [2].

Khối mã thứ hai của bạn là chính xác, giả định thuộc tính được khai báo như được mô tả ở trên.

p.s. Bạn không nên sử dụng [self.editMyObject release] theo phương thức -dealloc. Bạn nên gọi số [editMyObject release] (giả sử việc sao lưu ivar @property được gọi là editMyObject). Gọi accessor (thông qua self.editMyObject là an toàn cho các accessive @synthesized, nhưng nếu một accessor overriden dựa trên trạng thái đối tượng (có thể không hợp lệ tại vị trí gọi trong -dealloc hoặc gây ra các tác dụng phụ khác, bạn có một lỗi bằng cách gọi accessor.

[2] quy tắc quyền sở hữu đối tượng trong Cocoa rất đơn giản: nếu bạn gọi một phương pháp mà có alloc, hoặc copy trong chữ ký của mình (hoặc sử dụng +[NSObject new] mà về cơ bản là tương đương với [[NSObject alloc] init]), sau đó bạn "sở hữu" các đối tượng đó được trả lại và bạn phải cân bằng việc mua lại quyền sở hữu với số release. Trong tất cả các trường hợp khác, bạn không sở hữu đối tượng được trả về từ phương thức. với một số release.

+0

Tôi có NSMutableArray. Tôi đã thiết lập nó để sao chép nhưng khi tôi làm điều này [self.List addObject: myobject], tôi nhận được một ngoại lệ không bị bắt. Đặt nó trở lại để giữ lại hoạt động tốt. Bất kỳ đề xuất? – 4thSpace

+0

Tôi tin rằng một @property (bản sao) sẽ sử dụng bản sao để tạo bản sao, ngay cả khi gán mới là một loại có thể thay đổi.Vì vậy, bạn nhận được một bản sao bất biến (bạn thường sẽ sử dụng -mutableCopy để tạo bản sao có thể thay đổi), gây ra ngoại lệ khi bạn cố gắng thay đổi giá trị đã gán .... –

+0

[cont'd] Bạn có thể sử dụng @property (giữ lại) và gán như self.List = [[mutableArr mutableCopy] autorelease]. –

8

Thuộc tính của bạn được khai báo là "giữ lại" có nghĩa là thuộc tính được truyền trong đối tượng sẽ tự động được giữ lại.

Vì đối tượng của bạn đã có số tham chiếu một từ phân bổ/init thì có hai tham chiếu và tôi giả định chỉ có một bản phát hành (trong trình hủy của bạn).

Về cơ bản cuộc gọi đến self.editMyObject thực sự đang thực hiện việc này;

-(void) setEditMyObject:(MyObject*)obj 
{ 
    if (editMyObject) 
    { 
    [editMyObject release]; 
    editMyObject = nil; 
    } 

    editMyObject = [obj retain]; 
} 
+0

ngoại trừ rằng nếu 'editMyObject == obj', điều này có thể thất bại khủng khiếp, bởi vì bạn muốn phát hành đối tượng (có khả năng deallocating nó), và sau đó cố gắng giữ lại một con trỏ deallocated. –

3

Phiên bản đầu tiên tạo đối tượng không có bản phát hành phù hợp. Khi bạn phân bổ đối tượng, nó có nghĩa là bạn là chủ sở hữu của đối tượng đó. Setter của bạn có lẽ giữ lại đối tượng (như nó cần), có nghĩa là bây giờ bạn sở hữu đối tượng hai lần. Bạn cần bản phát hành để cân bằng việc tạo đối tượng.

Bạn nên đọc the Cocoa memory management guide nếu bạn dự định sử dụng Cocoa. Nó không phải là khó khăn một khi bạn tìm hiểu nó, nhưng nó là cái gì bạn phải học hoặc bạn sẽ có rất nhiều vấn đề như thế này.

1

Mọi người khác đã đề cập lý do tại sao nó gây ra rò rỉ bộ nhớ, vì vậy tôi sẽ chỉ kêu vang trong với làm thế nào để tránh được 'tạm' biến và vẫn ngăn chặn rò rỉ bộ nhớ:

self.editMyObject = [[[MyObject alloc] init] autorelease]; 

này sẽ để lại cho bạn (giữ lại) tài sản là chủ sở hữu duy nhất của đối tượng mới. Kết quả chính xác giống như ví dụ thứ hai của bạn, nhưng không có đối tượng tạm thời.

+0

Trong ứng dụng dành cho máy tính để bàn, tôi thường làm điều này. Tuy nhiên, trên iPhone (hoặc bất kỳ môi trường bị hạn chế bộ nhớ nào), việc sử dụng tính năng tự động phát lại có thể khiến việc sử dụng bộ nhớ tăng đột biến một cách không cần thiết. Stick với giữ lại/phát hành, trừ khi bạn cần autorelease. –

+0

Hmm. Tôi chắc chắn đến từ phía máy tính để bàn về điều này. Bạn có thể giải thích nguyên nhân gây ra sự tăng đột biến không? Dường như với tôi rằng không có bộ nhớ bổ sung nào được cấp phát trong trường hợp này, nhưng trải nghiệm mã hóa iPhone của tôi bị giới hạn. –

+0

Tôi không biết chi tiết nhưng nó cũng được ghi nhận rằng autorelease là một cuộc gọi khá đắt tiền trên iPhone. – 4thSpace

4

Theo quy ước trong Cocoa và Cocoa-touch, bất kỳ đối tượng nào được tạo bằng cách sử dụng [[SomeClass alloc] initX] hoặc [SomeClass newX] được tạo với số lần giữ lại. Bạn chịu trách nhiệm gọi [someClassInstance release] khi bạn đã hoàn tất với phiên bản mới của mình, thường là trong phương thức dealloc của bạn.

Trường hợp này trở nên khó khăn là khi bạn chỉ định đối tượng mới của mình cho thuộc tính thay vì biến mẫu. Hầu hết các thuộc tính được định nghĩa là retain hoặc copy, có nghĩa là chúng sẽ tăng số lượng lưu giữ của đối tượng khi được đặt hoặc tạo bản sao của đối tượng, để nguyên bản gốc.

Trong ví dụ của bạn, bạn có thể có này trong tập tin .h của bạn:

@property (retain) MyObject *editMyObject; 

Vì vậy, trong ví dụ đầu tiên của bạn:

// (2) property setter increments retain count to 2 
self.editMyObject = 

    // (1) new object created with retain count of 1 
    [[MyObject alloc] init]; 

// oops! retain count is now 2 

Khi bạn tạo Ví dụ mới của bạn MyObject sử dụng alloc/init, nó có số lần giữ lại. Khi bạn gán phiên bản mới cho self.editMyObject, bạn đang thực sự gọi phương thức -setEditMyObject: mà trình biên dịch tạo cho bạn khi bạn @synthesize editMyObject. Khi trình biên dịch thấy self.editMyObject = x, trình biên dịch sẽ thay thế nó bằng [self setEditMyObject: x].

Trong ví dụ thứ hai của bạn:

MyObject *temp = [[MyObject alloc] init]; 
// (1) new object created with retain count of 1 

self.editMyObject = temp; 
// (2) equivalent to [self setEditMyObject: temp]; 
// increments retain count to 2 

[temp release]; 
// (3) decrements retain count to 1 

bạn giữ cho đối tượng mới của bạn đủ dài để phát hành nó, do đó giữ lại đếm được cân bằng (giả sử bạn phát hành nó trong phương thức dealloc của bạn).

Xem thêm Cocoa strategy for pointer/memory management

0

Nó được đồng ý và giải thích rằng đoạn code dưới đây không có một sự rò rỉ (giả sử @property duy trì và @synthesize cho editMyObject):

//does not create memory leak 
MyObject *temp = [[MyObject alloc] init]; 
self.editMyObject = tempt; 
[temp release]; 

Câu hỏi: là bất cứ điều gì sai với mã sau đây không sử dụng một con trỏ temp?

//does not create memory leak ? 
self.editMyObject = [[MyObject alloc] init]; 
[editMyObject release]; 

Với tôi điều này có vẻ ổn.

+0

bạn cần phải chắc chắn rằng trong phương pháp dealloc của bạn rằng đối tượng được phát hành cũng: [editMyObject release] hoặc self.editMyObject = nil; – jyavenard

Các vấn đề liên quan