Как я могу предотвратить выпуск объектов при использовании Objective-C ARC с Xcode 4.2?

ETA: См. Внизу дополнительную информацию, которую я получил при профилировании приложения.

У меня есть приложение для iPhone, которое я только что преобразовал для использования ARC, и теперь я получаю несколько ошибок из-за объектов-зомби. До перехода я вручную их сохранял, и все было нормально. Я не могу понять, почему ARC их не сохраняет. Объекты объявляются как сильные свойства и указываются в точечной нотации.Это происходит в нескольких местах, поэтому я думаю, что где-то у меня есть фундаментальное непонимание ARC / управления памятью.

Вот пример, который особенно расстраивает. У меня есть NSMutableArray из 3 объектов. Каждый из этих объектов имеет свойство, которое также является NSMutableArray, которое в этом случае всегда имеет один объект. Наконец, этот объект имеет свойство, которое освобождается. Причина, по которой это расстраивает, заключается в том, что это происходит только с 3-м объектом из исходного массива. Первые 2 объекта всегда в полном порядке. Для меня просто не имеет смысла, как свойство одного объекта будет освобождено, если одно и то же свойство похожих объектов, созданных и используемых таким же образом, не является.

Массив хранится как свойство в UITableViewController:

@interface GenSchedController : UITableViewController <SectionHeaderViewDelegate>

@property (nonatomic, strong) NSArray *classes;

@end

@implementation GenSchedController

@synthesize classes;

Объекты, которые хранятся в массиве classes , определяются как:

@interface SchoolClass : NSObject <NSCopying, NSCoding>

@property (nonatomic, strong) NSMutableArray *schedules;

@end

@implementation SchoolClass

@synthesize schedules;

Объекты, хранящиеся в расписаниях Массив определяется как:

@interface Schedule : NSObject <NSCopying, NSCoding>

@property (nonatomic, strong) NSMutableArray *daysOfWeek;

@implementation Schedule

@synthesize daysOfWeek;

daysOfWeek - это то, что выпускается. Он просто содержит несколько NSStrings.

Я вижу, что во время viewDidLoad все объекты в порядке, без зомби. Однако, когда я касаюсь одной из ячеек таблицы и устанавливаю точку останова на первой строке tableView: didSelectRowAtIndexPath: , она уже была выпущена. Конкретная строка, которая вызывает ошибку, - это @synthesize daysOfWeek; , которая вызывается после 3-го цикла «for» ниже:

for (SchoolClass *currentClass in self.classes) {
    for (Schedule *currentSched in currentClass.schedules) {
        for (NSString *day in currentSched.daysOfWeek)

Но, опять же, это происходит только в последнем расписании последнего SchoolClass.

Может ли кто-нибудь указать мне правильное направление для правильной работы моего приложения с ARC?

В соответствии с просьбой, вот дополнительная информация. Во-первых, трассировка стека при возникновении исключения:

#0  0x01356657 in ___forwarding___ ()
#1  0x01356522 in __forwarding_prep_0___ ()
#2  0x00002613 in __arclite_objc_retainAutoreleaseReturnValue (obj=0x4e28b80) at /SourceCache/arclite_host/arclite-4/source/arclite.m:231
#3  0x0000d2fc in -[Schedule daysOfWeek] (self=0x4e28680, _cmd=0x220d6) at /Users/Jesse/Documents/Xcode/Class Test/Schedule.m:18
#4  0x0001c161 in -[SchedulesViewController doesScheduleOverlap:schedule2:withBufferMinutes:] (self=0x692b210, _cmd=0x22d58, schedule1=0x4e28680, schedule2=0x4e27f10, buffer=15) at /Users/Jesse/Documents/Xcode/Class Test/Classes/SchedulesViewController.m:27
#5  0x0001c776 in -[SchedulesViewController doesScheduleOverlap:schedule2:] (self=0x692b210, _cmd=0x22d9b, schedule1=0x4e28680, schedule2=0x4e27f10) at /Users/Jesse/Documents/Xcode/Class Test/Classes/SchedulesViewController.m:53
#6  0x0001cf8c in -[SchedulesViewController getAllowedSchedules] (self=0x692b210, _cmd=0x22dca) at /Users/Jesse/Documents/Xcode/Class Test/Classes/SchedulesViewController.m:78
#7  0x0001d764 in -[SchedulesViewController viewDidLoad] (self=0x692b210, _cmd=0x97cfd0) at /Users/Jesse/Documents/Xcode/Class Test/Classes/SchedulesViewController.m:121
#8  0x00620089 in -[UIViewController view] ()
#9  0x0061e482 in -[UIViewController contentScrollView] ()
#10 0x0062ef25 in -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:] ()
#11 0x0062d555 in -[UINavigationController _layoutViewController:] ()
#12 0x0062e7aa in -[UINavigationController _startTransition:fromViewController:toViewController:] ()
#13 0x0062932a in -[UINavigationController _startDeferredTransitionIfNeeded] ()
#14 0x00630562 in -[UINavigationController pushViewController:transition:forceImmediate:] ()
#15 0x006291c4 in -[UINavigationController pushViewController:animated:] ()
#16 0x000115d5 in -[GenSchedController tableView:didSelectRowAtIndexPath:] (self=0x4c57b00, _cmd=0x9ac1b0, tableView=0x511c800, indexPath=0x4e2cb40) at /Users/Jesse/Documents/Xcode/Class Test/Classes/GenSchedController.m:234
#17 0x005e7b68 in -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] ()
#18 0x005ddb05 in -[UITableView _userSelectRowAtPendingSelectionIndexPath:] ()
#19 0x002ef79e in __NSFireDelayedPerform ()
#20 0x013c68c3 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
#21 0x013c7e74 in __CFRunLoopDoTimer ()
#22 0x013242c9 in __CFRunLoopRun ()
#23 0x01323840 in CFRunLoopRunSpecific ()
#24 0x01323761 in CFRunLoopRunInMode ()
#25 0x01aa71c4 in GSEventRunModal ()
#26 0x01aa7289 in GSEventRun ()
#27 0x0057ec93 in UIApplicationMain ()
#28 0x0000278d in main (argc=1, argv=0xbffff5fc) at /Users/Jesse/Documents/Xcode/Class Test/main.m:16

И точное исключение - Class Test [82054: b903] *** - [__ NSArrayM RespondsToSelector:]: сообщение, отправленное в освобожденный экземпляр 0x4e28b80

И вот код, в котором все создается, загружается с диска:

NSString *documentsDirectory = [FileManager getPrivateDocsDir];

NSError *error;
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:&error];

// Create SchoolClass for each file
NSMutableArray *classesTemp = [NSMutableArray arrayWithCapacity:files.count];
for (NSString *file in files) {
    if ([file.pathExtension compare:@"sched" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
        NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:file];

        NSData *codedData = [[NSData alloc] initWithContentsOfFile:fullPath];
        if (codedData == nil) break;

        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:codedData];
        SchoolClass *class = [unarchiver decodeObjectForKey:@"class"];    
        [unarchiver finishDecoding];

        class.filePath = fullPath;

        [classesTemp addObject:class];
    }
}

self.classes = classesTemp;

Методы initWithCoder: действительно просты. Сначала для SchoolClass:

- (id)initWithCoder:(NSCoder *)decoder {
    self.name = [decoder decodeObjectForKey:@"name"];
    self.description = [decoder decodeObjectForKey:@"description"];
    self.schedules = [decoder decodeObjectForKey:@"schedules"];

    return self;
}

И для расписания:

- (id)initWithCoder:(NSCoder *)decoder {
    self.classID = [decoder decodeObjectForKey:@"id"];
    self.startTime = [decoder decodeObjectForKey:@"startTime"];
    self.endTime = [decoder decodeObjectForKey:@"endTime"];
    self.daysOfWeek = [decoder decodeObjectForKey:@"daysOfWeek"];

    return self;
}

Я попытался запустить Профиль в приложении, используя шаблон Zombies, и сравнил объект с избыточным выпуском с одним из других в массиве, который в порядке. Я вижу, что в строке для (NSString * day в currentSched.daysOfWeek) он переходит в геттер daysOfWeek, который сохраняет автозапуск . Затем, после того, как он возвращается из получателя, он выполняет еще одно сохранение (предположительно для удержания владения во время обработки цикла), а затем выпуск . Все это то же самое для проблемного объекта, что и для здорового объекта. Разница в том, что сразу после выпуска проблемный объект снова вызывает выпуск . На самом деле это не вызывает проблемы сразу, потому что пул автозапуска еще не опустошен, но как только это произойдет, счетчик сохраненных данных упадет до 0, и, конечно же, в следующий раз, когда я попытаюсь получить к нему доступ, это будет зомби.

Я не могу понять, ПОЧЕМУ там вызывается дополнительный выпуск .Из-за внешних циклов for количество вызовов currentSched.daysOfWeek действительно варьируется - он вызывается 3 раза для проблемного объекта и 5 раз для исправного объекта, но дополнительный release возникает при первом вызове, поэтому я не уверен, как это повлияет на него.

Помогает ли эта дополнительная информация понять, что происходит?

6
задан Elezar 14 December 2011 в 07:12
поделиться