Управление памятью с помощью NSThread - PullRequest
1 голос
/ 27 сентября 2011

У меня есть приложение, которое должно непрерывно сигнализировать слово в азбуке Морзе. Я сделал это, создав NSThread и запустив некоторый код внутри селектора с помощью цикла while. Вот код:

@implementation MorseCode


-(void)startContinuousMorseBroadcast:(NSString *)words{
    if (!(threadIsOn)) {

    threadIsOn = YES; s

    myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadSelector:) object:words];

    [myThread start];

}

if (morseIsOn) {
    morseIsOn = NO;
}
else{
    morseIsOn = YES;
}


   }

-(void)threadSelector:(NSString *)words{  

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

while (![myThread isCancelled]) {

//    ///it Does some code here


} //end While


NSLog(@"Cleaning the pool");
    [pool drain];
}


@end

При выходе из приложения (пользователь нажимает кнопку), в applicationDidEnterBackground выполняется следующий селектор:

-(void)cleanUpMorseObject{ //this is defined in the MorseCode class, same as threadSelector
    if (threadIsOn) {

        NSLog(@"cleanUpMorseObject, threadIsOn");
        threadIsOn = NO;
        morseIsOn = NO;

        [myThread cancel];
        [myThread release];

    }
}

Приложение правильно реагирует на событие, я проверил с помощью nslog. И тогда вызывается [выпуск MorseCode]. Код выглядит так:

-(void)applicationDidEnterBackground{ //this happens in the ViewController
[theMorse cleanUpMorseObject]; //theMorse is an instance of MorseCode
[theMorse release];
}

Проблема: хотя я вызываю [myThread release] , а затем [theMorse release] retainCount theMorse по-прежнему выше 0 (он не вызывает dealloc).

Инструмент «Утечки» не говорит о том, что у меня есть утечка, но если я открою и закрою приложение примерно 10 раз, то Iphone будет сброшен. Также в отладчике в конце концов я вижу предупреждение о получении памяти. Level = 2” .

Кроме того, я никогда не вижу NSLog до слива бассейна…

Приложение не работает в фоновом режиме. Есть идеи? Спасибо

Ответы [ 3 ]

4 голосов
/ 28 сентября 2011

Вы действительно должны запланировать отправку сообщения в RunLoop, вероятно, самый простой способ - это запланировать таймер (бесконечное повторение и короткий период повторения, например FLT_EPSILON или аналогичный) вместо использования потоков для этого.

Работа с потоками сложна, и каждый должен этого избегать (как Apple заявляет в своем Руководстве по программированию параллелизма и, как говорится в большинстве документов, «Потоки - это зло»;)).

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

  • Использовать уже существующие механизмы (например, асинхронную реализацию некоторых операций и сигнализировать с помощью методов делегатов или уведомлений), если доступно
  • Используйте методы, такие как performInBackground:
  • Используйте NSOperationQueues
  • Используйте GCD
  • И только в крайнем случае, и если нет других вариантов (или для действительно особых случаев), используйте NSThread.

Это избавит вас от многих проблем, так как все другие, более высокие API позаботятся о многих вещах для вас.


Более того, используяпотоки для этой задачи, как и вы, скорее всего, потребляют гораздо больше ресурсов ЦП (вероятно, быстро достигнут 100% использования), так как для планировщика задач не останется времени (вот почему даже GCD, который позаботится обо всех подобных вещах)это намного лучше, чем NSThreads, а планирование отправки в RunLoop еще лучше для CPU, если вам не нужны строгие ограничения RT)

3 голосов
/ 27 сентября 2011

Во-первых, retainCount никогда не сможет вернуть 0. Это бесполезный метод. Не называй это.

Во-вторых, утечки обнаруживают только объекты, на которые больше нет ссылок. Если поток все еще работает, он не просочился.

Наконец, поток не останавливается, когда вы вызываете cancel. Он просто устанавливает флаг, который вы должны проверить с помощью isCancelled, чтобы узнать, пора ли прекратить работу в потоке. Ты это делаешь?

<Ч />

ОК - легкий ответ. Следующий? Попробуйте построить и проанализировать. Затем используйте инструмент Allocations и включите отслеживание количества ссылок. Затем посмотрите, что зовет retain дополнительное время.

0 голосов
/ 05 октября 2011

Я решил отказаться от класса NSThread и использовал другой подход:

-(void)playSOSMorse{

    if ([myTimer isValid]) {

        [myTimer invalidate];
        [myTimer release];
        myTimer = nil;
    }

        myTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001
                                                    target:self
                                                  selector:@selector(tymerSelector)
                                                  userInfo:nil
                                                   repeats:NO] retain];
    //the timer calls a selector that performs a selector in background

}

-(void)tymerSelector{

    [self performSelectorInBackground:@selector(threadSelector2) withObject:nil];  
}

-(void)threadSelector2   {

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

//some code here

 [pool drain];

//calls another selector on the main thread to see if it needs to fire the timer again and restart the cycle
 [self performSelectorOnMainThread:@selector(selectorOnMainThread) withObject:nil waitUntilDone:NO];

}

-(void)selectorOnMainThread{
        [myTimer invalidate];
        [myTimer release];


    myTimer = nil;


    if (morseIsOn) { //this is a boolean that if it is true (YES) calls the timer again

        [self playSOSMorse];  

    }

}

Надеюсь, это кому-нибудь поможет :) Спасибо

...