Комментарии 80
Я бы во втором вопросе NSMutableDictionary использовал.
Типа:
1. Сплитим строку по разделителям
2. Циклом по токенам и dictionary[token] = dictionary[token] + 1. Предварительно token можно конечно лоуэркейсить (например).
3. Ну и выбрать N самых частых слов (после сортировки)
Что-то подсказывает, что и по размеру код был бы меньше.
Как вариант можно с использованием Heap (кучи) решить.
Типа:
1. Сплитим строку по разделителям
2. Циклом по токенам и dictionary[token] = dictionary[token] + 1. Предварительно token можно конечно лоуэркейсить (например).
3. Ну и выбрать N самых частых слов (после сортировки)
Что-то подсказывает, что и по размеру код был бы меньше.
Модификаций алгоритма для выбора N значений мне не известны.
Как вариант можно с использованием Heap (кучи) решить.
+3
В предоженном мной варианте нет сортировки. И если N существенно меньше количества различных слов на входе, мой вариант будет намного быстрее. Если бы вы предложили законченный код, можно было бы сравнить быстродействие.
-1
а как же indexToInsert? он наверняка не мгновенный
+2
Ваше решение работает за O(m log m + n^2), где m — длина входной строки. Или O(m + n^2), если NSCountedSet построен на хеш-таблице.
Решение же с кучей работает за O(m log m + n log n), что всегда быстрее.
Решение же с кучей работает за O(m log m + n log n), что всегда быстрее.
+1
Сразу скажу, что я абсолютно не имею никакого отношения к программированию под iOS, но тем не менее у меня есть некоторые замечания к вашим решениям:
1. Довольно спорный момент. Возможно они наоборот пытаются отсеять любителей регулярок. Задача довольно простая и легко решается без использования регулярных выражений (но честно говоря я бы всё равно сделал регулярку, но значительно короче чем у вас)
4. По моему с магическим числом вы немного не угадали: судя по размеру числа его автор преследовал цель сделать «гарантированно продолжительный цикл». Считаю, что правильным будет заменить цикл на while с условием выхода по "[operation isCancelled]"
4. По моему вместо магического числа должен быть размер массива, а не константа или еще что-то в этом роде.
5. Три запроса на выборку данных из 2 таблиц? Это не серьёзно. Почитайте о JOIN. У вас запрос станет проще и читабельнее
UPD: Изменил 4 пункт, я немного не правильно прочитал код
1. Довольно спорный момент. Возможно они наоборот пытаются отсеять любителей регулярок. Задача довольно простая и легко решается без использования регулярных выражений (но честно говоря я бы всё равно сделал регулярку, но значительно короче чем у вас)
4. По моему с магическим числом вы немного не угадали: судя по размеру числа его автор преследовал цель сделать «гарантированно продолжительный цикл». Считаю, что правильным будет заменить цикл на while с условием выхода по "[operation isCancelled]"
4. По моему вместо магического числа должен быть размер массива, а не константа или еще что-то в этом роде.
5. Три запроса на выборку данных из 2 таблиц? Это не серьёзно. Почитайте о JOIN. У вас запрос станет проще и читабельнее
UPD: Изменил 4 пункт, я немного не правильно прочитал код
+4
По поводу «заменить цикл на while с условием выхода по [operation isCancelled]». Вы не в теме. Это не условие выхода из цикла. NSOperation — это кусок какой-то работы, который нужно выполнить. Обычно его добавляют в очередь, откуда эти операции выполняются по одному или параллельно, в зависимости от очередей и приоритетов. Операция может быть отменена. Длительно выполняющаяся операция должна периодически проверять не отменили ли ее и в случае, если отменили, как можно быстрее прекратить работу. Но, возможно, я вас не правильно понял. Вообще, любой for можно заменить на while — это вопрос вкуса.
0
Я бы во втором задании просто отсортировал бы массив count'ов:
Первое задание решал бы тоже regexp'ом, единственное что — не очень понятно что за \\A и \\z по бокам стоят. Ну и {0, 1} на "?" заменить бы.
NSMutableArray *wordsByNumbersArray = [NSMutableArray array];
[set enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
[wordsByNumbersArray addObject:@{@"word": obj,
@"count": @([set countForObject:obj])}];
}];
[wordsByNumbersArray sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"count"
ascending:NO]]];
return [wordsByNumbersArray subarrayWithRange:NSMakeRange(0, N)]
Первое задание решал бы тоже regexp'ом, единственное что — не очень понятно что за \\A и \\z по бокам стоят. Ну и {0, 1} на "?" заменить бы.
0
задача 2
NSString *wordsString = @"test test hello world test hello";
NSArray *words = [wordsString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSCountedSet *wordsSet = [NSCountedSet setWithArray:words];
NSArray *uniqueWords = [wordsSet allObjects];
NSArray *sortedWords = [uniqueWords sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSUInteger count1 = [wordsSet countForObject:obj1];
NSUInteger count2 = [wordsSet countForObject:obj2];
return [@(count2) compare:@(count1)];
}];
NSArray *result = [sortedWords subarrayWithRange:NSMakeRange(0, MAX([sortedWords count], 3))];
0
А если после некоторых слов будут запятые или другие символы?
0
Ну можно punctuationCharacterSet еще приделать.
Вообще никак не могу найти хорошую статью про токенизацию строк, казалось что на NSHipster было, но как будто бы нету. Там рассказывалось как по-уму делить на слова (и не только) в разных локалях.
Вообще никак не могу найти хорошую статью про токенизацию строк, казалось что на NSHipster было, но как будто бы нету. Там рассказывалось как по-уму делить на слова (и не только) в разных локалях.
0
Улучшенная версия
NSString *wordsString = @"test, test hello! world test... hello";
NSCountedSet *wordsSet = [NSCountedSet set];
[wordsString enumerateSubstringsInRange:NSMakeRange(0, [wordsString length])
options:NSStringEnumerationByWords
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[wordsSet addObject:substring];
}];
NSArray *uniqueWords = [wordsSet allObjects];
NSArray *sortedWords = [uniqueWords sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSUInteger count1 = [wordsSet countForObject:obj1];
NSUInteger count2 = [wordsSet countForObject:obj2];
return [@(count2) compare:@(count1)];
}];
NSArray *result = [sortedWords subarrayWithRange:NSMakeRange(0, MAX([sortedWords count], 3))];
0
в первом задании вроде как можно объединить a-zA-Z в a-Z, внутри квадратных скобок точку и дефис можно не экранировать. Не предусмотрено, что длина может быть 1 символ
0
Предусмотрено — там {0,1} в самом конце.
0
Да, извиняюсь, как-то проглядел. Но всё равно можно оптимизировать, не только длину, но и читаемость — избавиться от «или» (|). Сейчас если соберусь с силами, то попробую переписать. Как минимум, можно {0,1} заменить на?
0
раз caseInsensitive, то можно совсем убрать эти A-Z, использовать \d вместо 0-9.
тестировал в консоли девелопера в хроме
получилось так, но чувствую подвох
тестировал в консоли девелопера в хроме
/^[a-z]([a-z\d.-]{0,18}[a-z\d])?$/i
получилось так, но чувствую подвох
0
Предусмотрено, что длина может быть в 1 символ. В «статье» есть ссылка — можете загрузить проект приложения и проверить.
0
Задача 5
не шибко силен в SQL, но наверное как-то так
Призываю знатоков, интересно увидеть хорошее решение
не шибко силен в SQL, но наверное как-то так
SELECT `tracks`.`name`
FROM `track_downloads`
LEFT JOIN `tracks` ON `tracks`.`id` = `track_downloads`.`track_id`
GROUP BY `track_downloads`.`track_id`
WHERE COUNT(`track_downloads`.`track_id`) > 1000
Призываю знатоков, интересно увидеть хорошее решение
0
Скорее уж так, хотя в деталях мог ошибиться
SELECT t.name, COUNT(d.track_id) as c
FROM track_downloads d
INNER JOIN tracks t on t.id = d.track_id
GROUP BY d.track_id
HAVING c > 1000
+1
Я обратил внимание, что надо селектнуть только имена, может это важно. Спасибо что напомнили про having.
А какой здесь профит от inner join?
А какой здесь профит от inner join?
0
в случае LEFT JOIN у вас могут быть записи не вошли во внутреннее соединение (NULL)
0
Это я понимаю, думал вдруг это влияет на производительность в лучшую сторону. Представил, что таблицы гарантированно полные и поэтому написал left.
0
Можно посмотреть и оценить как происходит запрос поставив перед SELECT в вашем и моем примере EXPLAIN
0
Explain я бы конечно воспользовался, но время позднее, лень создавать таблицу и тестовые данные. Может valeriyvan еще не удалил и попробует?
0
Я обратил внимание, что надо селектнуть только имена, может это важно.
Если HAVING используется то в SELECT нужно указать то что в GROUP BY или агрегированное значение, поэтому в SELECT есть COUNT(d.track_id)
0
SELECT
tracks.name AS "Track name",
Count(tracks.id) AS Downloads
FROM
track_downloads
INNER JOIN tracks ON track_downloads.track_id = tracks.id
GROUP BY
tracks.id
HAVING
Downloads > 1000
+1
В четвертом вопросе мне кажется важным, что operationQueue вызовет операцию на неглавном треде, где совсем нет autorelease pool, поэтому мы должны его сделать, чтобы process(data) всё освободило. Но queue может быть и mainQueue так-то.
0
НЛО прилетело и опубликовало эту надпись здесь
В каждом потоке есть NSAutoreleasePool.
Цитирую документацию: Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects (see “Threads”). As new pools are created, they get added to the top of the stack. When pools are deallocated, they are removed from the stack. Autoreleased objects are placed into the top autorelease pool for the current thread. When a thread terminates, it automatically drains all of the autorelease pools associated with itself.
Цитирую документацию: Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects (see “Threads”). As new pools are created, they get added to the top of the stack. When pools are deallocated, they are removed from the stack. Autoreleased objects are placed into the top autorelease pool for the current thread. When a thread terminates, it automatically drains all of the autorelease pools associated with itself.
0
Какая-то довольно мутная формулировка если честно.
Ну и приходит в голову мысль, что NSOperationQueue придерживает пул тредов, которые не terminate, а просто засыпают.
Возможно, я тут что-то путаю, но от этой части мануала у меня больше вопросов, чем ответов.
Ну и приходит в голову мысль, что NSOperationQueue придерживает пул тредов, которые не terminate, а просто засыпают.
Возможно, я тут что-то путаю, но от этой части мануала у меня больше вопросов, чем ответов.
0
Цитирую документацию: Note: In iOS 4 and later, operation queues use Grand Central Dispatch to execute operations. Prior to iOS 4, they create separate threads for non-concurrent operations and launch concurrent operations from the current thread. For a discussion of the difference between concurrent and non-concurrent operations and how they are executed, see NSOperation Class Reference.
Для OSX: Operation queues usually provide the threads used to run their operations. In OS X v10.6 and later, operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations. As a result, operations are always executed on a separate thread, regardless of whether they are designated as concurrent or non-concurrent operations. In OS X v10.5, however, operations are executed on separate threads only if their isConcurrent method returns NO. If that method returns YES, the operation object is expected to create its own thread (or start some asynchronous operation); the queue does not provide a thread for it.
Смысл такой, что GCD (Grand Central Dispatch) создает потоки только по необходимости, утилизируя те потоки что у него есть. NSOperationQueue в новых системах работает через GCD.
Для OSX: Operation queues usually provide the threads used to run their operations. In OS X v10.6 and later, operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations. As a result, operations are always executed on a separate thread, regardless of whether they are designated as concurrent or non-concurrent operations. In OS X v10.5, however, operations are executed on separate threads only if their isConcurrent method returns NO. If that method returns YES, the operation object is expected to create its own thread (or start some asynchronous operation); the queue does not provide a thread for it.
Смысл такой, что GCD (Grand Central Dispatch) создает потоки только по необходимости, утилизируя те потоки что у него есть. NSOperationQueue в новых системах работает через GCD.
0
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Отвечу по пунктам.
0
Отвечу по пунктам.
1. Что не понравилось с error не понял. Да, если не нужен код ошибки, можно было передать NULL вместо &error. Результат сравнения имеет тип int, а не BOOL. В данном конкретном случае явное приведение к BOOL избыточно. В реальном коде я использовал не функцию а блок, который передавал дальше. поэтому без приведения была ошибка несоответствия типа блоков.
2. Кода будет меньше, но появится сортировка. У меня нет сортировки. Да, есть многократный поиск, но поиск, во-первых по массиву длины N (в задании этого нет, но я предполагал, что N значительно меньше количества слов на входе), во-вторых, поиск этот выполняется по упорядоченному массиву.
3. Мне показалось, что тот кто писал вопрос или не знал об этом методе или имел в виду что-то другое, чего я не понял.
4. Цитата из документации: Important: If you use Automatic Reference Counting (ARC), you cannot use autorelease pools directly. Instead, you use @autoreleasepool blocks. В ARC коде вы можете или завернуть что вам нужно в @autoreleasepool или не делать этого. В не ARC коде вы можете «ручками» каждую сотую итерацию цикла сделать [pool release]; pool = [[NSAutoreleasePool alloc] init];
1. Что не понравилось с error не понял. Да, если не нужен код ошибки, можно было передать NULL вместо &error. Результат сравнения имеет тип int, а не BOOL. В данном конкретном случае явное приведение к BOOL избыточно. В реальном коде я использовал не функцию а блок, который передавал дальше. поэтому без приведения была ошибка несоответствия типа блоков.
2. Кода будет меньше, но появится сортировка. У меня нет сортировки. Да, есть многократный поиск, но поиск, во-первых по массиву длины N (в задании этого нет, но я предполагал, что N значительно меньше количества слов на входе), во-вторых, поиск этот выполняется по упорядоченному массиву.
3. Мне показалось, что тот кто писал вопрос или не знал об этом методе или имел в виду что-то другое, чего я не понял.
4. Цитата из документации: Important: If you use Automatic Reference Counting (ARC), you cannot use autorelease pools directly. Instead, you use @autoreleasepool blocks. В ARC коде вы можете или завернуть что вам нужно в @autoreleasepool или не делать этого. В не ARC коде вы можете «ручками» каждую сотую итерацию цикла сделать [pool release]; pool = [[NSAutoreleasePool alloc] init];
0
НЛО прилетело и опубликовало эту надпись здесь
2. И почему в блоке не просто [n1 compare: n2]? Наверное, потому что я — тормоз. Другого ответа у меня нет.
4. Разумеется. И при каждой итерации NSAutoreleasePool будет уничтожаться и создаваться новый. А теперь создайте пул не каждую итерацию, а каждую сотую. Опять я тормоз. Cделать вложенный цикл, который завернуть в @autoreleasepool {}.
Из половины ответов чему-то да научился. Вот что значит поговорить с умными людьми.
4. Разумеется. И при каждой итерации NSAutoreleasePool будет уничтожаться и создаваться новый. А теперь создайте пул не каждую итерацию, а каждую сотую. Опять я тормоз. Cделать вложенный цикл, который завернуть в @autoreleasepool {}.
Из половины ответов чему-то да научился. Вот что значит поговорить с умными людьми.
+2
НЛО прилетело и опубликовало эту надпись здесь
Пулы под ARC эффективнее своих до-ARC собратьев, кстати. Если не ошибаюсь, создаются на стеке (де-факто являясь C++-объектами, в этом можно убедитья в исходниках Objective C runtime), в отличие от NSAutoreleasePool, которые являлись полноценными Objective C-объектами со всеми вытекающими (pun not intended).
0
> [pool release]; pool = [[NSAutoreleasePool alloc] init];
Лучше просто
Лучше просто
[pool drain];
+1
1. Зачем стараться решить одной регуляркой все задачи на свете разом? Если разбить на логические условия, то и вероятность ошибки меньше, и читабельнее. Кроме того, легче будет вносить изменения, когда удастся убедить начальство, что такие требования к паролю неразумны. :)
Что-то вроде:
Что-то вроде:
// Length
if (length < 1 || length > 20) return NO;
// First character
if (!checkRegEx(@"\\A[a-z]")) return NO;
// Last character
if (!checkRegEx(@"[a-z\\d]\\z")) return NO;
// General character set
if (!checkRegEx(@"[a-z\\d-.]\\z")) return NO;
+1
Дальше пробежал по-диагонали, но бросилось в глаза про блоки в п. 4.
Использование
[operation addExecutionBlock:^{
...
if ([operation isCancelled]) return;
...
}];
Использование
__weak
здесь не опционально, а строго обязательно! Ибо operation
будет захвачена блоком, а блок будет захвачен operation
. Классический retain cycle и утечка памяти.+2
Да, и ещё надо внимательно посмотреть, что есть
queue
и data[i]
. Ибо если это переменные экземпляра, то произойдёт неявная подстановка self->data[i]
, и одного __weak
для operation
будет недостаточно, т.к. self
проретейнится блоком, и получится более сложный retain цикл self > queue > operation > block > self.+1
НЛО прилетело и опубликовало эту надпись здесь
У меня, вообще-то, изначально так и было. Но после долгого медитирования (=гугления), решил что это излишне. На эту тему есть противоположные мнения. См., например, ответы здесь. Я, вообще, предпочитаю, делать так, как учит Apple на WWDC. Один из ответов по ссылке выше ссылаются на сессию WWDC2012, и мне кажется, я даже был на ней. Вполне возможно, что LLVM генерирует правильный код в любом случае.
Пересмотрю сессию WWDC. Перечитаю документацию. Эта тема с блоками и операциями и как оно, все таки, правильно, вполне может быть темой отдельной статьи.
Пересмотрю сессию WWDC. Перечитаю документацию. Эта тема с блоками и операциями и как оно, все таки, правильно, вполне может быть темой отдельной статьи.
0
Что-то я не понял, как медитация и гугление привели Вас к неправильному решению. :) На том же SO в правильном ответе используется __weak, и в принятом в итоге тоже (изначально он был неверен, но это указано в UPDATE).
Да и в той же сессии WWDC объясняется, откуда берется retain цикл:
и как его обойти:
Здесь двух мнений быть не может. Использование __weak обязательно!
Но всегда лучше проверить на практике.
Для этого возьмём упрощенный пример без __weak, сразу после добавления операции очистим очередь, а через какое-то время проверим слабую ссылку на операцию на предмет утечки:
Как видим, получился retain цикл, и операция осталась в памяти.
Теперь в блоке воспользуемся слабой ссылкой на операцию:
Retain цикл разорвался, и память освободилась.
Теперь вернём data из примера, и представим, что это (как и queue) — переменные экземпляра:
Создадим этот экзмепляр, запустим очередь и операцию с блоком, а через 1.5 секунды его прибьём, ожидая, что очередь остановится:
Однако как мы видим очередь не остановилась. Это потому, что
И получаем нужное поведение. Всё? Не совсем. А что будет, если код обработки у нас будет чуть сложнее, и произведёт пару retain/autorelease для нашего экземпляра? Проверим.
Как мы и предположили, объект продолжил жить дольше, чем нам нужно. Решаем проблему с помощью
Остался только один маленький нюанс. Проверка
Ой. Кажется получилось довольно длинно. Извиняюсь. :)
Да и в той же сессии WWDC объясняется, откуда берется retain цикл:
и как его обойти:
Здесь двух мнений быть не может. Использование __weak обязательно!
Но всегда лучше проверить на практике.
Для этого возьмём упрощенный пример без __weak, сразу после добавления операции очистим очередь, а через какое-то время проверим слабую ссылку на операцию на предмет утечки:
Вспомогательный макрос для примера
#define after(DELAY, BLOCK) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, \
DELAY * NSEC_PER_SEC), \
dispatch_get_main_queue(), BLOCK)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = [[NSBlockOperation alloc] init];
[op addExecutionBlock:^{
for (int i = 0; i < 5; ++i) {
if ([op isCancelled]) break; // <- strong
NSLog(@"i = %d;", i);
}
}];
[queue addOperation:op];
[queue cancelAllOperations];
__weak NSBlockOperation *weakOperation = op;
after(0.5, ^{
NSLog(@"Operation: %p", weakOperation);
});
// Operation: 0x7527ae0
Как видим, получился retain цикл, и операция осталась в памяти.
Теперь в блоке воспользуемся слабой ссылкой на операцию:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = op;
[op addExecutionBlock:^{
for (int i = 0; i < 5; ++i) {
if ([weakOperation isCancelled]) break; // <- weak
NSLog(@"i = %d;", i);
}
}];
[queue addOperation:op];
[queue cancelAllOperations];
after(0.5, ^{
NSLog(@"Operation: %p", weakOperation);
});
// Operation: 0x0
Retain цикл разорвался, и память освободилась.
Теперь вернём data из примера, и представим, что это (как и queue) — переменные экземпляра:
@implementation TTTestRetainCycle{
NSOperationQueue *_queue; // <-
NSArray *_data; // <-
}
-(void)dealloc{
[_queue cancelAllOperations];
}
-(void)start{
_queue = [[NSOperationQueue alloc] init];
_data = @[@1, @2, @3, @4, @5];
NSBlockOperation *op = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = op;
[op addExecutionBlock:^{
for (int i = 0; i < 5; ++i) {
if ([weakOperation isCancelled]) break;
NSLog(@"data[%d] = %d;", i, [_data[i] integerValue]); // <-
sleep(1); // <-
}
}];
[_queue addOperation:op];
}
@end
Создадим этот экзмепляр, запустим очередь и операцию с блоком, а через 1.5 секунды его прибьём, ожидая, что очередь остановится:
__block TTTestRetainCycle *test = [TTTestRetainCycle new];
[test start];
after(1.5, ^{
NSLog(@"Stopping");
test = nil;
});
// data[0] = 1;
// data[1] = 2;
// Stopping
// data[2] = 3;
// data[3] = 4;
// data[4] = 5;
Однако как мы видим очередь не остановилась. Это потому, что
_data[i]
в блоке неявно разворачивается в self->_data[i]
и блок захватывает self. В результате, как я уже упоминал в сообщении выше, получается retain cycle: self -> _queue -> operation -> block -> self. Для разрыва цикла нужно использовать слабую ссылку weakSelf->_data[i]
, однако разыменовывание слабых указателей запрещено. Значит, классически создаём strong указатель из weak:-(void)start{
// ...
__typeof(self) __weak weakSelf = self;
[op addExecutionBlock:^{
for (int i = 0; i < 5; ++i) {
if ([weakOperation isCancelled]) break;
__typeof(self) strongSelf = weakSelf; // <-
if (!strongSelf) break; // <-
NSLog(@"data[%d] = %d;", i, [strongSelf->_data[i] integerValue]); // <-
sleep(1);
}
}];
[_queue addOperation:op];
}
// data[0] = 1;
// data[1] = 2;
// Stopping
И получаем нужное поведение. Всё? Не совсем. А что будет, если код обработки у нас будет чуть сложнее, и произведёт пару retain/autorelease для нашего экземпляра? Проверим.
Вспомогательный класс, компилируется с -fno-objc-arc
@implementation TTRetainer
+(void)retain:(id)object{
[object retain];
}
+(void)autorelease:(id)object{
[object autorelease];
}
@end
// ...
[op addExecutionBlock:^{
for (int i = 0; i < 5; ++i) {
if ([weakOperation isCancelled]) break;
__typeof(self) strongSelf = weakSelf;
if (!strongSelf) break;
[TTRetainer retain:strongSelf]; // <-
[TTRetainer autorelease:strongSelf]; // <-
NSLog(@"data[%d] = %d;", 0, [strongSelf->_data[0] integerValue]);
sleep(1);
}
}];
// ...
// data[0] = 1;
// data[0] = 1;
// Stopping
// data[0] = 1;
// data[0] = 1;
// data[0] = 1;
Как мы и предположили, объект продолжил жить дольше, чем нам нужно. Решаем проблему с помощью
@autoreleasepool
: // ...
[op addExecutionBlock:^{
for (int i = 0; i < 5; ++i) {
@autoreleasepool { // <-
if ([weakOperation isCancelled]) break;
__typeof(self) strongSelf = weakSelf;
if (!strongSelf) break;
[TTRetainer retain:strongSelf];
[TTRetainer autorelease:strongSelf];
NSLog(@"data[%d] = %d;", 0, [strongSelf->_data[0] integerValue]);
sleep(1);
}
}
}];
// ...
// data[0] = 1;
// data[0] = 1;
// Stopping
Остался только один маленький нюанс. Проверка
[weakOperation isCancelled]
при продеаллоченной операции ошибочно вернёт NO и цикл продолжит выполнение. Однако мне не удалось воспроизвести ситуацию, что объект NSBlockOperation удалён из памяти, а его цикл всё ещё выполняется. Но поскольку такое поведение незадокументировано и не гарантируется (поправьте, если не так), то лично я всё равно создал бы strong указатель из weakOperation и проверял if (strongOperation && [strongOperation isCancelled])
.Ой. Кажется получилось довольно длинно. Извиняюсь. :)
+2
Первое задание еще можно решить с помощью NSPredicate и регулярки.
- (BOOL)isLoginValid:(NSString *)login {
NSString *regExp = @"\\A[a-zA-Z](([a-zA-Z0-9\\.\\-]{0,18}[a-zA-Z0-9])|[a-zA-Z0-9]){0,1}\\z";
NSPredicate *testPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regExp];
return [testPredicate evaluateWithObject:login];
}
0
Еще возможный вариант для второго задания. Работает медленнее, но читаемость выше, как мне кажется.
- (NSArray *)mostFriquentWordsInString:(NSString *)inputString count:(int)count {
NSMutableArray *words = [[inputString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] mutableCopy];
NSMutableArray *counts = [NSMutableArray new];
while ([words count] > 0) {
NSString *word = [words firstObject];
NSArray *foundWords = [words filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF == %@", word]];
[words removeObjectsInArray:foundWords];
[counts addObject:@{
@"word" : word,
@"wordCount" : @([foundWords count])
}];
}
[counts sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"wordCount" ascending:NO]]];
return [counts subarrayWithRange:NSMakeRange(0, (MIN(count, [counts count])))];
}
0
Теперь в Яндекс отправится куча резюме с шаблонными решениями на эту позицию.
Даже не знаю, хорошо это или плохо.
С учетом того, что субъективно, это худшее приложение от Яндекса в apple store, с точки зрения качества работы.
Даже не знаю, хорошо это или плохо.
С учетом того, что субъективно, это худшее приложение от Яндекса в apple store, с точки зрения качества работы.
0
НЛО прилетело и опубликовало эту надпись здесь
До iOS 7, возможно, это был private api. developer.apple.com/library/ios/releasenotes/General/iOS70APIDiffs/ секция Foundation.
0
Удивило, что слово «музыка» используется только в заголовке.
0
Регулярка чудная.
* Конечно же не нужно a-zA-Z если case insensivitive
* 0-9 можно заменить на \d, однако из-за обилия экранов это иногда только ухудшает читаемость. Кстати, не знаком с обжСи, нельзя ли их поменьше сделать? И почему там вообще делимитеры совпадают с экранами? Делимитеры лучше выбирать такими, чтобы не только не путались с экранами, но и с тем, что будем мэтчить.
* Если в группе должен быть дефис, то можно написать его в конце, не экранируя [a-z-]
* Лимит на 20 символов проще проверить вне регулярки, чем городить конструкции вида {0,18}, которые зачастую весьма ненадежны в граничных случаях
* не указаны начало-конец строки, может сматчить середину логина, удовлетворяющую условиям
Вот мой вариант:
Длину проверяем отдельно.
* Конечно же не нужно a-zA-Z если case insensivitive
* 0-9 можно заменить на \d, однако из-за обилия экранов это иногда только ухудшает читаемость. Кстати, не знаком с обжСи, нельзя ли их поменьше сделать? И почему там вообще делимитеры совпадают с экранами? Делимитеры лучше выбирать такими, чтобы не только не путались с экранами, но и с тем, что будем мэтчить.
* Если в группе должен быть дефис, то можно написать его в конце, не экранируя [a-z-]
* Лимит на 20 символов проще проверить вне регулярки, чем городить конструкции вида {0,18}, которые зачастую весьма ненадежны в граничных случаях
* не указаны начало-конец строки, может сматчить середину логина, удовлетворяющую условиям
Вот мой вариант:
#^[a-z](?:[a-z\d\.-]*[a-z\d]$|$)#siu
Длину проверяем отдельно.
-2
в методе
полнейшее пренебрежение к утечкам памяти.
во первых:
mutableCopy увеличивает счетчик ссылок на 1
во вторых:
раз уж на то пошло, то нужно возвращать
-(NSArray*)mostFrequentWordsInString:(NSString*)string count:(NSUInteger)count
полнейшее пренебрежение к утечкам памяти.
во первых:
NSMutableCharacterSet *separators = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
mutableCopy увеличивает счетчик ссылок на 1
во вторых:
return [selectedWords copy];
раз уж на то пошло, то нужно возвращать
return [[selectedWords copy] autorelease];
-1
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Задания для разработчика Яндекс.Музыки для iOS