Фоновый поток NSRunloop run не завершается после аннулирования NSTimer! Зачем?

Я создаю NSTimer и добавляю его в цикл запуска фонового потока. Мой код похож на пример фонового потока для этого ответа: iPhone-SDK: вызывать функцию в фоновом режиме?

После создания таймера и привязки его к runloop из GDB I po runLoop и это выводит это:

<CFRunLoop 0x695b090 [0x16a62c0]>{wakeup port = 0x6907, stopped = false,
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x6936e60 [0x16a62c0]>{type = mutable set, count = 1,
entries =>
    1 : <CFString 0x16abba8 [0x16a62c0]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = <CFBasicHash 0x695e160 [0x16a62c0]>{type = mutable set, count = 1,
entries =>
    0 : <CFRunLoopTimer 0x69398a0 [0x16a62c0]>{valid = Yes, interval = 6, next fire date = 329774303, callout = __NSFireTimer (0x212399), context = <CFRunLoopTimer context 0x6903a10>}
}
,
modes = <CFBasicHash 0x6904120 [0x16a62c0]>{type = mutable set, count = 1,
entries =>
    1 : <CFRunLoopMode 0x6946180 [0x16a62c0]>{name = kCFRunLoopDefaultMode, port set = 0x6807, timer port = 0x6b03, 
    sources0 = (null),
    sources1 = (null),
    observers = (null),
    timers = <CFArray 0x695e180 [0x16a62c0]>{type = mutable-small, count = 1, values = (
    0 : <CFRunLoopTimer 0x69398a0 [0x16a62c0]>{valid = Yes, interval = 6, next fire date = 329774303, callout = __NSFireTimer (0x212399), context = <CFRunLoopTimer context 0x6903a10>}
)}
},

}
}

Это показывает, что 1 таймер подключен к циклу запуска. Позже, после того как я аннулировал таймер, метод запуска NSRunloop не завершается, но после приостановки отладчика и из GDB I po runLoop опять это выглядит так:

<CFRunLoop 0x695b090 [0x16a62c0]>{wakeup port = 0x6907, stopped = false,
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x6936e60 [0x16a62c0]>{type = mutable set, count = 1,
entries =>
    1 : <CFString 0x16abba8 [0x16a62c0]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = <CFBasicHash 0x695e160 [0x16a62c0]>{type = mutable set, count = 0,
entries =>
}
,
modes = <CFBasicHash 0x6904120 [0x16a62c0]>{type = mutable set, count = 1,
entries =>
    1 : <CFRunLoopMode 0x6946180 [0x16a62c0]>{name = kCFRunLoopDefaultMode, port set = 0x6807, timer port = 0x6b03, 
    sources0 = (null),
    sources1 = (null),
    observers = (null),
    timers = <CFArray 0x695e180 [0x16a62c0]>{type = mutable-small, count = 0, values = ()}
},

}
}

Теперь запись "таймеры" содержит 0 объектов. Но поток продолжает работать. Я часто покидаю экран, затем возвращаюсь, так что это приводит к созданию фоновых потоков, которые в конечном итоге убивают приложение после слишком большого количества ресурсов. Таймеры не срабатывают после их аннулирования, но фоновый поток остается.

Я знаю, что могу переместить таймеры обратно в основной поток или создать свой собственный простой поток таймеров с помощью NSThread sleepForTimeInterval, но я бы хотел сохранить основной поток для обновлений графического интерфейса и использовать NSTimer, если это возможно.

3 ответа

Это останавливает цикл выполнения для меня:

[[NSRunLoop currentRunLoop] runUntilDate: [NSDate date]];

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

// Kill the runloop now.
NSRunLoop* rl = [NSRunLoop currentRunLoop]; // Get the runloop
if (rl != [NSRunLoop mainRunLoop])
{
    // Set it running again, but only until now. 
    // In other words, STOP!!!
    [rl runUntilDate: [NSDate date]];
}

От -[NSRunLoop run] документы:

Удаление всех известных входных источников и таймеров из цикла выполнения вручную не гарантирует выхода из цикла выполнения.

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

Мне нужно было прочитать документацию для [NSTimer invalidate].... Этот метод должен вызываться из того же потока, в котором установлен таймер. Если вы не вызываете его из того же потока, поток таймера не завершится.

Другие вопросы по тегам