6

Tôi mới sử dụng ARC nhưng hiểu cách hoạt động và tôi đang dùng thử. Tôi đang trên iOS nên bộ nhớ là một mối quan tâm nghiêm trọng.Cách buộc phát hành trên iOS

Tôi có lớp MyObject chứa nhiều dữ liệu lớn. Tôi muốn phát hành nó và tải một bộ dữ liệu mới.

MyObject *object; 
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1 

// later... 
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2 

Điều này làm việc tốt mà không bị rò rỉ, và tôi đoán ARC chèn [phát hành đối tượng] trước bài tập mới. Vấn đề của tôi là dữ liệu bên trong 'đối tượng' được phát hành sau khi bộ mới được cấp phát và tôi hết bộ nhớ. Điều tôi thực sự muốn có thể làm là:

object = nil; 

<function to pop the pool, wait till everything is deallocated> 

object = [MyObject alloc] initWithData:folder2]; // load data from folder2 

nhưng tôi không chắc chắn cách thực hiện điều đó. Tôi có thể chạy phân bổ mới trên một performselector afterdelay, nhưng nó cảm thấy như tôi đang chụp trong bóng tối và một chút hack. Có thể là cách thích hợp để thực hiện việc này?

PS Tôi đã thử tìm kiếm câu trả lời, nhưng tất cả kết quả là về rò rỉ bộ nhớ và cách đảm bảo các biến nằm ngoài phạm vi và đặt biến thành nil, v.v. điều thời gian.

CẬP NHẬT
Cảm ơn câu trả lời, tôi đã muốn thử

object = nil; 
object = [MyObject alloc] initWithData:folder2]; 

và nó đã không làm việc. Tôi không chắc liệu nó có được cho là có hay không. Bây giờ tôi hiểu rằng nó phải làm việc, nhưng tôi phải có một cái gì đó khác giữ cho nó cho rằng phần của một giây. Tôi có NSLogs trong tất cả các phương thức init/dealloc của tôi, và tôi có thể thấy đầu tiên tất cả các trường hợp của các lớp mới (của các thanh ngang của MyObject) được gọi, và sau đó gần như ngay sau (trong vài ms), dealloc của MyObject , tiếp theo là deallocs của các ivars của nó. Tôi cũng đã thử @autorelease nhưng điều tương tự cũng xảy ra.

Tôi đã tìm kiếm trong suốt dự án và dán tất cả mã mà tôi nghĩ có thể có liên quan đến điều này.

@interface AppDelegate : UIResponder <UIApplicationDelegate>; 
    @property PBSoundSession *soundSession; 
@end 


//-------------------------------------------------------------- 
@implementation AppDelegate 

// onTimer fired at 60Hz 
-(void)onTimer:(NSTimer *) theTimer { 
    [oscReceiver readIncoming]; // check incoming OSC messages 
    // then do a bunch of stuff with _soundSession; 
} 
@end 


//-------------------------------------------------------------- 
@implementation OscReceiver 

-(void)readIncoming { 
    AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; 

    // parse all incoming messages 

    if(bLoadNewSoundBank) { 
     NSString *newFolder = parseNewFolder(); 
     appDelegate.soundSession = nil; 
     appDelegate.soundSession = [MyObject alloc] initWithData:newFolder]; 
    } 
} 

@end 


//-------------------------------------------------------------- 
@implementation GuiController 

// onTimer fired at 10Hz 
-(void)onTimer:(NSTimer *) theTimer { 
    PBSoundSession *soundSession = appDelegate.soundSession; 
    // update gui with received values 
} 

@end 

tôi nghĩ nó có thể là biến cục bộ 'soundSession' trong GuiController :: OnTimer giữ vào appDelegate.soundSession cũ trong suốt thời gian của phương pháp đó, nhưng tôi ngạc nhiên cho ý kiến ​​ra tất cả các mã GUI (trong thực tế vô hiệu hóa bộ đếm thời gian), không có sự khác biệt.

Có cách nào để tìm ra tại thời điểm đó, người vẫn đang nắm giữ ứng dụng của tôiDelegate.soundSession không? Tôi đặt một điểm dừng nơi tôi đặt nó thành không, nhưng không thể tìm thấy bất kỳ thông tin hữu ích nào. Tôi đã thử mẫu Thiết bị phân bổ dụng cụ, nhưng không thể tìm thấy bất kỳ thứ gì hữu ích ở đó (có lẽ vì tôi không biết phải tìm đâu).

Đây là những gì phân bổ theo dõi của tôi trông giống như, bạn có thể thấy bộ nhớ là tất cả deallocated một chút quá muộn! Valid XHTML.

+0

Vâng, bạn sẽ nhận được nhiều thứ bạn muốn bằng cách chỉ cần thực hiện 'object = nil;' trước khi bạn cấp phát/init đối tượng mới. –

Trả lời

10

Đây có thể không phải là sự cố ARC. Những gì bạn có thể nhìn thấy là autorelease pool của bạn không thoát ra đủ sớm — MyObject của bạn đang được phát hành, nhưng dữ liệu mà nó tải được giữ bởi hồ bơi vì một số cặp nội bộ -retain/-autorelease.Hãy thử gói -initWithData: cuộc gọi của bạn trong một khối @autoreleasepool, như thế này:

@autoreleasepool { 
    object = [[MyObject alloc] initWithData:folder1]; 
    // do things 
} 
// later… 
@autoreleasepool { 
    object = [[MyObject alloc] initWitData:folder2]; 
    // do other things 
} 

Thiết lập đối tượng để nil ngay lập tức trước khi đặt nó vào cái gì khác như Gabriele cho thấy có thể gây ra các trình biên dịch để chèn các phiên bản phù hợp trước khi thứ hai -alloc/-initWithData: , nhưng nó có thể đủ thông minh để làm điều đó - nếu điều đó không hiệu quả, rất có thể đó là điều tự động sửa chữa.

+0

Tôi đã sửa đổi câu trả lời của mình. Nó chắc chắn giải phóng bộ nhớ ** trước khi ** con trỏ được gán lại, nếu không một rò rỉ bộ nhớ sẽ xảy ra vì sẽ không có tham chiếu đến phần như vậy của bộ nhớ. –

+0

Phải — Tôi nói rằng có thể điều đó xảy ra ** ngay lập tức ** trước khi gán lại, tức là sau '-alloc' /' -initWithData thứ hai ', nhưng trước khi' đối tượng' được đặt thành kết quả, trong trường hợp đó trong giây lát sẽ là hai MyObject trong bộ nhớ. –

+0

Điểm tốt, tôi phải suy nghĩ về nó –

3

Không có sự chậm trễ khi thoát @autoreleasepool {...}; các đối tượng trong hồ bơi có số release được gọi ngay lập tức. Nếu một đối tượng tồn tại, đó là bởi vì có một tài liệu tham khảo strong ở nơi khác hoặc vì đối tượng là autorelease d vào hồ bơi tiếp theo.

Nếu bạn làm:

a = [[Foo alloc] initBigThing]; 
a = nil; 
a = [[Foo alloc] initBigThing]; 

Các trường hợp đầu tiên của Foo sẽ được phát hành trước khi việc phân bổ thứ hai

Với một caveat lớn; nếu bất kỳ đường dẫn mã nào mà a được gọi khi xảy ra với retain/autorelease, thì nó sẽ dính xung quanh cho đến khi hồ được thoát. Xung quanh nó trong @autoreleasepool{ ... }; nên làm các trick.

Lưu ý rằng trình biên dịch đôi khi sẽ phát ra các chuỗi retain/autorelease trong các bản dựng không được tối ưu hóa được loại bỏ trong các bản dựng được tối ưu hóa.

1

Một cắn câu trả lời tổng quát hơn, tôi thấy làm thế nào bạn có thể buộc thả một đối tượng:

#import <objc/message.h> 

// --- 

while ([[object valueForKey:@"retainCount"] integerValue] > 1) { 
    objc_msgSend(object, NSSelectorFromString(@"release")); 
} 
objc_msgSend(object, NSSelectorFromString(@"release")); 

Nhưng bạn không nên làm điều này vì ARC lẽ sẽ phát hành các đối tượng sau và điều này sẽ gây ra một vụ tai nạn. Phương pháp này chỉ nên được sử dụng trong gỡ lỗi!