Страницы

Поиск по вопросам

понедельник, 15 октября 2018 г.

Важна ли видеокарта для сервера?

Есть обычный компьютер (года 2008 выпуска) в качестве сервера на базе Windows 7 x64. Воткнут в розетку и с интернетом. Все подключения к нему осуществляются через RDP если необходимо что-то запустить/настроить. В остальное время молотит без участия пользователя. Хочется максимально снизить шум и тепловыделение. Встроенного видео нет. Другие матери, из имеющихся, оборудованы меньшим количеством разъёмов под оперативку.
Отсюда встал вопрос:
Какие минимальные требования к видеокарте для RDP подключений (максимум 2 подключения)?
Что имею:
PCI-E GeForce 7600GT (стоит в данный момент и шумит), какая-то PCI плата с 1-2МБ памяти из конца 90-х (используется для восстановления BIOS видеокарт). Что-то типа такого S3 Trio


Ответ

RDP не использует ресурсы видео карты, подключается через собственный виртуальный драйвер. Но для запуска самого сервера видео карта нужна. (При определенных условиях компьютер/сервер можно запустить и без видео-карты).
... На сервере RDP использует собственный видеодрайвер для визуализации вывода, создавая информацию рендеринга в сетевых пакетах с использованием протокола RDP и отправки их по сети клиенту. На клиенте RDP получает данные рендеринга и интерпретирует пакеты в соответствии с вызовами API интерфейса графического устройства Microsoft Windows (GDI). Для входного пути события клиентской мыши и клавиатуры перенаправляются от клиента к серверу. На сервере RDP использует свой собственный драйвер клавиатуры и мыши для приема этих событий клавиатуры и мыши. ...
о протоколе RDP

Влияние словарей JavaScript на производительность

Я привык добавлять словари в код для создания пространства имен: var namespace = { // Какой-то код somefunc: function () { },
// Какие-то данные somedata: undefined } Потому что данные и функции удобно разделяются: namespace.somefunc(); namespace.somedata = ""; Но мне не совсем ясно, как это искусственное разделение влияет на производительность? Насколько затормаживаются вызовы и обращения?


Ответ

Почти не влияет. Всего лишь 1 доп. поиск в map (все объекты в js являются картами, или map. Вызов namespace.somedata - ни что иное, как window['namespace']['somedata'], против window['somedata'] без нс). Это самая элементарная операция, их масса во время выполнения кода. Зато плюсы (в больших скриптах не засоряется глобальное пространство имен) очевидны.

SVG недостатки

SVG, я с этим форматом особа не разбирался, только на уровне статьи из вики и небольших экспериментов. Мне интересно, почему обладая столькими достоинствами такими как: маштабируемость, открытость и т.д. он не получил большого распространения, ведь недостатки-то не настолько существенны чтобы его не применять. Объясните, пожалуйста, в чём его недостатки и достоинства. P.S.Мне это нужно по двум причинал: сдать лабу и понять что лучше мне использовать на своём сайте для анимаций(пока думаю, что svg).


Ответ

Причины, по которым SVG не получил до сих пор широкого распространения (хотя ситуация меняется с появлением HTML5), не связана с его техническими особенностями. Так получилось. Во-первых, сегментация рынка обозревателей препятствует одновременной поддержке разными производителями даже технологий, поддерживаемых стандартами W3C: синхронизация действий разных производителей занимает много времени. Во-вторых, у всех участников рынка существуют свои коммерческие интересы, и часто получается проще и выгоднее сделать что-то свое, чем поддерживать единый стандарт: как результат появляются технологии вроде Silverlight и Flash.

Нейронная сеть на Python. Что это?

Вопрос касается изучения Нейронных сетей на Python. Причем, для человека, который понятия не имеет, что такое и как работают данные сети. Google не дал ни одной полезной ссылки, ни одной вменяемой статьи, где русским языком для не сведущего человека обьяснили, что это такое и с чем его едят. Я, конечно, немного утрирую, и после обобщенного экскурса в биологию можно представить себе, как работают нейронные сети в общих чертах, но тут начинается: "Нейронные имеют невыпуклую функцию вероятности, что приводит к проблеме локальных максимумов", "Инициализируем матрицу весов случайными данными", "Создадим сеть из 10 нейронов, 20 слоев и 100500 входов", "Подадим входной сигнал и научим сеть отличать темное пиво от светлого" и т.д. и т.п. Никто и нигде не объясняет, почему для конкретной задачи, требуется именно сколько нейронов, два, а не восемьдесят слоев, какие именно данные и в каком виде поступают на вход? Сколько килограмм взвешивать на весах? Это тайна или я совсем тупой?Объясните...


Ответ

Никто и нигде не объясняет, почему для конкретной задачи, требуется именно сколько нейронов, два, а не восемьдесят слоев Это одна из задач, которую пока решает человек методом подбора исходя из своего опыта в проектировании нейросетей. (Другая серьезная задача - это способ обучения) По нейросетям могу порекомендовать отличное введение Что касается Python, для начала, чтобы поиграться, вам хватит PyBrain - это, на данный, момент, как мне кажется, самая простая библиотека.

Objective-C: как (возможно ли) гарантированно исчерпать main run loop?

Этот вопрос адресован участникам, хорошо знакомым с Cucumber и с проектами типа Selenium, Capybara и другими, так как именно они в первую очередь поймут специфику данного вопроса.
Вот оригинальный пост автора библиотеки Frank
Writing iOS acceptance tests using Kiwi - Being Agile
В этом посте рассматривается возможность написания Acceptance tests средствами самого Objective-C с использованием лишь Xcode's Application Testing Target (см. соотв. раздел "Setting Up Application Unit Tests" в документации Apple) и пары библиотек (PublicAutomation и Shelley, которые обеспечивают связь с UIAutomation). Оказалось, что такая возможность существует и подход, описанный в этой статье работает прекрасно.
Вот код, в котором содержится то, что описано в этой статье (ссылка на него лежит в самом конце странице статьи, в комментариях).
Следующий отрывок кода, содержащегося в этой статье, содержит метод, производящий нажатие на объект класса UIView, заданный с помощью селектора.
- (void)tapViewViaSelector:(NSString *)viewSelector{ [UIAutomationBridge tapView:[self viewViaSelector:viewSelector]]; sleepFor(0.1); //ugh }
Обратите внимание на строку sleepFor(0.1); //ugh. О ней-то и пойдёт речь в данном вопросе:
Если вы посмотрите репозиторий на Github, вы увидите, что за ней скрывается следующая дефиниция
#define sleepFor(interval) (CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, false))
Данная строка - это наивная (не в смысле наивности автора, а в смысле это первое простое решение, которое пришло бы и мне в голову) попытка автора дождаться исчерпания главной Run loop, крутящейся в главном потоке (те, кто это знают, - знают), перед тем, как перейти к следующему действию.
Пример возможной последовательности UI interactions, который продемонстрирует наглядно, о чём идёт речь:
Я на экране логина приложения. Я нажимаю (tap) текстовое поле ввода E-mail адреса (Всплывает клавиатура) Я ввожу текст, нажимаю Enter (Клавиатура скрывается) Я нажимаю (tap) текстовое поле ввода Password. (Всплывает клавиатура) Я ввожу текст, нажимаю Enter (Клавиатура скрывается) Я нажимаю кнопку "Войти" (происходит запрос к серверу про аутентификацию, в случае успеха происходит насыщенный событиями переход на главный экран приложения) Я должен увидеть UILabel, содержащий текст "Вы находитесь на главной странице"
Описанный сценарий полагается на описанные в статье хелперы и
ЕСЛИ убрать sleepFor() из всех кода всех интеракций, стоящих за каждым из описанных действий (нажатия, вводы текстовых полей, swipe gestures и всё-всё остальное), то каждое следующее действие не будет дожидаться окончания анимаций, transitions и прочих действией, стоящих за текущим шагом и требующих времени, так как они не блокируют главный поток, а записываются на выполнения (being scheduled) в главную петлю главного потока (main thread's run loop).
Простой пример: не дождавшись пропадания клавиатуры от предыдущего поля, в момент её пропадания, -[UIAutomationBridge tapViewViaSelector:] будет опираться на промежуточную координату поля, в которое нужно будет ввести значение и таким образом клик(тап) не сработает по адресу. Таких примеров можно привести бесчисленное множество (например, I should eventually see UILabel named "Some text" on a main screen).
Итак, ЗАДАЧА:
Написать хэлпер, который с наименьшим временем ожидания, с наименьшим количеством пустых прогонов main run loop и соответственно с наименьшим количеством пустых CPU циклов, обеспечит гарантированное ожидание момента, пока main run loop не будет исчерпана, чтобы можно было переходить к следующему шагу test scenario
ПРИЛОЖЕНИЕ 1
Вот мой мой текущий промежуточный код, который работает в силу того, что он написан in a paranoid fashion:
// DON'T like it static inline void runLoopIfNeeded() { // https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFRunLoopRef/Reference/reference.html
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource); // DON'T like it if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource) runLoopIfNeeded(); }
// DON'T like it static inline BOOL eventually(BOOL(^eventualBlock)(void)) { NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:10];
runLoopIfNeeded();
while (eventualBlock() == NO) { if ([timeoutDate compare:[NSDate date]] == NSOrderedAscending) { @throw [NSException exceptionWithName:NSGenericException reason:@"Wait timeout has expired" userInfo:nil]; }
runLoopIfNeeded(); }
runLoopIfNeeded();
return YES; }
Вот следующее промежуточное решение:
// It is much better, than it was, but still unsure static inline void runLoopIfNeeded() { // https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFRunLoopRef/Reference/reference.html
__block BOOL flag = NO;
// https://stackoverflow.com/questions/7356820/specify-to-call-someting-when-main-thread-is-idle dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ flag = YES; }); });
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource);
if (flag == NO) runLoopIfNeeded(); }
ПРИЛОЖЕНИЕ 2
В момент написания этого вопроса, я подумал о том, что возможны приложения, которые устанавливают таймеры (или Run loop sources) да так, что искомая функция никогда не сможет исчерпать главный поток, но давайте будем считать, что ничего экстравагантного не происходит, и __всегда наступает, такой момент, когда петля начинает крутиться вхолостую, ожидая поступления какого-либо действия исключительно от пользователя (то есть CFRunLoopRunInMode начинает стабильно возвращать kCFRunLoopRunHandledSource. A source was processed. Я пока что ни разу не встречал исключений - это всегда наступает.)
ПРИЛОЖЕНИЕ 3
Я даже просто буду рад увидеть любой дельный комментарий по рассматриваемому вопросу от коллег, знающих об этих вещах лучше, чем я.
ПРИЛОЖЕНИЕ 4
Я бы отдал всю свою репутацию за исчерпывающий ответ на этот вопрос. Скорее всего, мы сможем договориться с модераторами об этом ;)
ПРИЛОЖЕНИЕ 5
Это простейший пример, показывающий необходимость исчерпания main run loop. Только, пожалуйста, не подумайте, что, если ваш вариант runLoopIfNeeded сработает для этого примера, то задача решена: в реальном приложении в main run loop может быть назначено такое количество всякой всячины, что ваш метод будет спотыкаться об их количество, продолжая главный поток значительно раньше, чем вам нужно. Я проверяю свой runLoopIfNeeded на своём iOS-приложении, на нём же я буду проверять ваш вариант.
Итак, простейший пример:
dispatch_async(^{ // ... NSLog(@"Completed"); });
runLoopIfNeeded(); // Нужно, чтобы главный поток останавливался на этой строке, продолжая крутить при этом главную петлю (main run loop), дожидаясь пока в консоли появится completed.
NSLog(@"I want to be called exclusively AFTER the moment when animation becomes completed");


Ответ

Публикую своё текущее решение (если интересно, оно здесь):
static inline void runLoopIfNeeded() { // https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFRunLoopRef/Reference/reference.html
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource); }
Поздний комментарий: Если сравнить это решение с промежуточными решениями (их было два, см. ПРИЛОЖЕНИЕ 1 в вопросе), то станет видно, что все три очень похожи, но это последнее состоит вообще из одной строки - это результат нескольких прояснений (см. ниже прояснение 3) и небольшого обсуждения в топике параллельно открытом на SO (см. конец вопроса).
В ходе расследования прояснилось несколько вариантов поведения, о которых я не знал, а точнее просто никогда не было времени подумать над ними:
Первый случай Оказывается, методы типа +[UIView animateWithDuration:...] запускают свои анимации не в главном потоке (об этом написано здесь, в разделе Starting Animations Using the Block-Based Methods), а дочернем. Отсюда вытекает, что их невозможно "исчерпать" даже правильно написанным методом runLoopIfNeeded()
Пример:
[UIView animateWithDuration:10 animations:^{ self.view.backgroundColor = [UIColor redColor]; // в течение 10 секунд будем краснеть } completion:^(BOOL finished) { NSLog(@"Completion called"); }];
runLoopIfNeeded(); // <- (*)
(*) Сколько не бейся над его имплементацией, а поймать момент completion не удастся, если только специально не крутить run loop 10 и более секунд, чего нельзя делать в runLoopIfNeeded(), иначе он, очевидно, перестанет быть хелпером общего назначения.
Второй случай, который невозможно "вычерпывать" с помощью runLoopIfNeeded это конструкции типа:
double delayInSeconds = 10.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // <#code to be executed on the main queue after delay#> });
Я многократно проверил - они записываются куда-то не туда, откуда (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource) может вернуть YES. То есть эти отложенные запуски посредством dispatch_after совершенно невидимы для runLoopIfNeeded основанного на CFRunLoopRunInMode. Даже не думаем о том, чтобы ловить такое в принципе посредством runLoopIfNeeded
Третий случай, очень интересный
dispatch_async поддаётся исчерпанию с помощью (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES). Что это значит? Опять же простой пример (обратите внимание, я использую массив для регистрации вхождений вместо всяких NSLog, так как каждое присутствие NSLog в критичном коде, требуя 1 цикла run loop, искажает эксперимент):
NSMutableArray *registry = [NSMutableArray new];
dispatch_async(dispatch_get_main_queue(), ^{ [registry addObject:@"main_queue"]; });
[registry addObject:@"before run loop"]; CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES); // Первый вариант запускаем так, а второй - комментируем эту строку [registry addObject:@"after run loop"];
NSLog(@"registry: %@", [registry componentsJoinedByString:@", "]);
Первый вариант: registry: before run loop, main_queue, after run loop Второй вариант: registry: before run loop, after run loop (то есть к моменту NSLog назначенный блок не выполнился)
Из этого третьего случая следует очень-очень интересное следствие:
Вам приходилось сталкиваться с необходимостью юнит-тестирования асинхронных сетевых запросов с использованием, скажем, библиотеки AFNetworking, ну или даже просто +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]?
На эту тему написано огромное количество топиков на SO. Я не могу вдаваться сейчас в подробности того, как это делается обычно, поэтому просто для знающих предмет покажу такой пример:
[someAsynchronousRequestWithCompletionHandler:^(id JSON){ // some test assertions on JSON }];
runLoopIfNeeded(); // на весь запрос может потребоваться 2-3 запуска CFRunLoopInMode // (*)
(*) так вот присутствия этой строки будет вполне достаточно, чтобы "выпрямить" асинхронный запрос, то есть дождаться его выполнения без использования всяких прожорливых на CPU-циклы циклов методов вроде
__block BOOL done = NO; [someAsynchronousRequestWithCompletionHandler:^(id JSON){ // some test assertions on JSON done = YES }]; while(done == NO) {}
или на секунды и требующих правильной настройки:
dispatch_semaphore_t sema = dispatch_semaphore_create(0); [someAsynchronousRequestWithCompletionHandler:^(id JSON){ // some test assertions on JSON
dispatch_semaphore_release(sema); }];
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:2]]; }
Теперь обратно к главному вопросу:
Описанные три случая и ещё столько же неописанных убедительно показали мне, что насколько бы ни был хорош метод runLoopIfNeeded(), невозможно знать гарантированно и наверняка, что ничего важного не происходит сейчас на экране, текущая реализация runLoopIfNeeded даёт, я полагаю навскидку, процентов 60% достоверности. Для того, чтобы все мои хелперы, похожие на аналогичные в Capybara заработали, мне понадобилось ввести дополнительный хелпер, который используя параноидальную стратегию проверяет самые разные утверждения на истинность:
// Is it possible to make it less paranoid? static inline BOOL eventually(BOOL(^eventualBlock)(void)) { NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:10];
runLoopIfNeeded();
while (eventualBlock() == NO) { if ([timeoutDate compare:[NSDate date]] == NSOrderedAscending) { @throw [NSException exceptionWithName:NSGenericException reason:@"Wait timeout has expired" userInfo:nil]; }
runLoopIfNeeded(); }
runLoopIfNeeded();
return YES; }
Так, например хэлпер для ввода текста в заданное текстовое поле с добавлением изрядной доли паранойи начинает выглядеть так:
#pragma mark #pragma mark Text fills
void fillTextFieldWithText(UITextField *textField, NSString *text) { runLoopIfNeeded();
tapView(textField);
BOOL keyboardAppeared = eventually(^BOOL{ return [UIAutomationBridge checkForKeyboard] && textField.isEditing; });
if (keyboardAppeared){ [UIAutomationBridge typeIntoKeyboard:text];
[textField endEditing:YES];
eventually(^BOOL{ return textField.isEditing == NO; }); }
runLoopIfNeeded(); }
Аналогичные хэлперы принимают такой же вид, и в результате я могу запускать тесты следующего вида без каких-либо проблем с тем, что что-то ещё не до конца появилось, не стало видно, не перестало работать и т.п.:
it(@"should...", ^{ tapButtonWithTitle(@"Зарегистрироваться");
[[theValue(RegistrationScreen.isCurrentScreen) should] beYes];
fillTextFieldWithText(RegistrationScreen.nameField, @"stanislaw"); fillTextFieldWithText(RegistrationScreen.emailField, @"s.pankevich@gmail.com"); fillTextFieldWithText(RegistrationScreen.passwordField, @"11111");
tapButtonWithTitle(@"Зарегистрироваться");
[[theValue(eventually(^{ return hasLabelWithText(@"Проверьте, пожалуйста, почту"); })) should] beYes];
tapButtonWithTitle(@"Готово");
[[theValue(LoginScreen.isCurrentScreen) should] beYes]; });
Те, кому приходилось сталкиваться со связкой Cucumber + Capybara наверняка увидят замечательную схожесть этого примера с тем, как подобные вещи пишутся на Capybara или уровнем ниже на Selenium
Если кому-то интересно, то я обернул всю эту логику в проект NativeAutomation, который выложил на Github.
ОБНОВЛЕНО ЕЩЁ ПОЗЖЕ: я только что получил первый ответ в топике, параллельно открытом на SO, по которому видно, что отвечающий мыслит точно в таком же направлении.

Обеспечит ли двойная шифровка AES-256 стойкость к взлому в 2 раза выше?

Хочу выяснить у людей разбирающихся в криптографии вот какой вопрос, ибо я не очень силён в таких делах.
Предположим у нас есть какие-то исходные данные размером 128 бит. Я их шифрую AES-256 с ключом key1, а затем, полученный шифр заново шифрую тем же AES-256, но для большей безопасности уже другим ключом key2.
Значит ли это что стойкость зашифрованной информации к взлом стала выше в 2 раза?


Ответ

В сущности - да, атакующему придётся подбирать оба ключа, чтобы расшифровать исходное сообщение. Т.к. изначально это было 2^256, и ещё добавили 2^256, в результате стало 2^512. Только не понятно, зачем Вам это нужно. Ключ длиной в 256 бит прямым перебором не берётся, нет смысла его увеличивать.

RecyclerView CheckBox

Знаю, что эта тема много раз поднималась для ListView, но ни как не могу разобраться с ней на примере RecyclerView
Есть RecyclerView, в котором повторяются CardView. RecyclerView заполняется адаптером. Когда отмечаю несколько чек-боксов, при скроллинге вниз, некоторые чек-боксы сами становятся отмеченными. Понял, что нужно описать этот момент в адаптере, но не нашел, где объясняется как правильно и почему. Подскажите пожалуйста. Вот код адаптера:
public class AddFilterAdapter extends RecyclerView.Adapter {
private ArrayList mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public CheckBox mCheckBox;
public ViewHolder(View v) { super(v); mTextView = (TextView) v.findViewById(R.id.tv_filter_card); mCheckBox = (CheckBox) v.findViewById(R.id.mcheckBox);
} }
public AddFilterAdapter(ArrayList dataset) { mDataset = dataset; }
@Override public AddFilterAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.add_filter_card, parent, false);
ViewHolder vh = new ViewHolder(v); return vh; }
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTextView.setText(mDataset.get(position)); }
@Override public int getItemCount() { return mDataset.size(); } }


Ответ

Дело в том, что айтемы переиспользуются и для корректной работы нужно принудительно сохранять изменения в чекбоксах в отдельное хранилище, а при выводе востанавливать значения из него.
Kласс RecyclerView не реализует никаких готовых методов для работы с чекбоксами, поэтому все нужно писать самому. Возможный вариант - завести отдельный массив булевых переменных и сохранять в него текущие состояния чекбоксов. На чекбоксы повешать слушатели, чтобы контролировать изменения состояний. При выводе в адаптере восстанавливать сохраненное ранее состояние.
public class AddFilterAdapter extends RecyclerView.Adapter {
private ArrayList mDataset; boolean[] checked;
public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public CheckBox mCheckBox;
public ViewHolder(View v) { super(v); mTextView = (TextView) v.findViewById(R.id.tv_filter_card); mCheckBox = (CheckBox) v.findViewById(R.id.mcheckBox);
} }
public AddFilterAdapter(ArrayList dataset) { mDataset = dataset; checked = new boolean[mDataset.size()]; }
@Override public AddFilterAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.add_filter_card, parent, false);
ViewHolder vh = new ViewHolder(v); return vh; }
@Override public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(mDataset.get(position)); holder.mCheckBox.setChecked(checked[position]); holder.mCheckBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { checked[position] = !checked[position]; } }); }
public boolean[] getChecked(){ return checked;
@Override public int getItemCount() { return mDataset.size(); } }
метод getChecked() вернет полученные значения из адаптера "наружу", например в активити, где эти данные можно обработать:
// код в активити для получения чекнутых значений. boolean[] checked = filterAdapter.getChecked();
Так же вы можете не заводить отдельный массив, а создать дополнительное поле в своей модели данных и писать сохранение состояния туда, так же оттуда и восстанавливать текущее состояние для вывода. Соответственно и возвращать через геттер коллекцию самих моделей. Это может быть полезно, когда требуется связать данные самой модели и их признак чекнутости.