- Что произойдет если сначала нажать на кнопку 1 а потом на кнопку 2?
- Что произойдет при исполнении следующего кода?
- Реализуйте следующие методы: retain, release, autorelease
- Выведется ли в дебагер «Hello world»? Почему?
- Что выведется в консоль?
- Задача про банерокрутилку
- Задача на регулярное выражение
- Метод, возвращающий N наиболее часто встречающихся слов во входной строке
- Перечислите все проблемы, которые вы видите в приведенном ниже коде. Предложите, как их исправить
- Заполнить строку буквами А, чтобы не делать миллионы итераций
- Написать процедуру, инвертирующую массив символов
- Что не так с этим кодом?
- Какой метод вызовется: класса A или класса B?
- int8_t matrix 2048 2024, допустимо ли?
- Одинаковую ли память занимают эти структуры и почему так?
- (IBAction)btn1DidTouch:(id)sender {
_string = [[[NSString alloc] initWithFormat:@"v=%i", rand()] autorelease];
}
- (IBAction)btn2DidTouch:(id)sender {
NSLog(@"_string is equal to ‘%@’", _string);
}
Крэш, т.к utoreleasepool создается в начале обработки события и уничтожается в конце. т.е. нажали на первую кнопку — началась обработка события, нажали на вторую — кнопку — закончилась обработка первого события, очистился autoreleasepool а вместе с ним и объект удалился, а ссылка осталась. Затем началась обработка второго события и мы обращаемся к задеалоченному объекту, результат — крэш.
Ball *ball = [[[[Ball alloc] init] autorelease] autorelease];
Впоследствии это вызовет крэш. Потому что объект дважды добавляется в пул автоосвобождения, и release
вызовется дважды.
- (id)retain {
NSIncrementExtraRefCount(self);
return self;
}
- (void)release {
if (NSDecrementExtraRefCountWasZero(self)) {
NSDeallocateObject(self);
}
}
- (id)autorelease {
// Add the object to the autorelease pool
[NSAutoreleasePool addObject:self];
return self;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Hello world");
});
/* Another implementation */
return YES;
}
Нет. deadlock
NSObject *object = [NSObject new];
dispatch_async(dispatch_get_main_queue(), ^ {
NSLog(@"A %d", [object retainCount]);
dispatch_async(dispatch_get_main_queue(), ^ {
NSLog(@"B %d", [object retainCount]);
});
NSLog(@"C %d", [object retainCount]);
});
NSLog(@"D %d", [object retainCount]);
D 2
A 2
C 3
B 2
Из заданного списка вывести поочередно, рандомно, а главное, без повторений, все его элементы.
import java.util.Random;
class Banner
{
public static void main(String[] args)
{
Random r = new Random();
int[] list = new int[100];// в угоду наглядности в дебри линкедлистов лезть не будем
for (int i = 0; i < 100; ++i)
{
list[i] = i;
}
/*А теперь главное - логика алгоритма*/
int index = 99;
int number;
while (index > 0)
{
number = r.nextInt(index);
System.out.println(list[number]);
list[number] = list[index];
--index;
}
}
}
Теперь кратко и по сути, что здесь происходит. Пока в списке есть элементы, мы берем случайное число в пространстве длины массива, выводим его, потом последний элемент ставим на место только что выведенного, а индекс длины уменьшаем на единицу, пока не останется ничего.
В системе авторизации есть следующее ограничение на формат логина: он должен начинаться с латинской буквы, может состоять из латинских букв, цифр, точки и минуса и должен заканчиваться латинской буквой или цифрой. Минимальная длина логина — 1 символ. Максимальная — 20 символов.
BOOL loginTester(NSString* login) {
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\A[a-zA-Z](([a-zA-Z0-9\\.\\-]{0,18}[a-zA-Z0-9])|[a-zA-Z0-9]){0,1}\\z" options:NSRegularExpressionCaseInsensitive error:&error];
// Здесь надо бы проверить ошибки, но если регулярное выражение оттестированное и
// не из пользовательского ввода - можно пренебречь.
NSRange rangeOfFirstMatch = [regex rangeOfFirstMatchInString:login options:0 range:NSMakeRange(0, [login length])];
return (BOOL)(rangeOfFirstMatch.location!=NSNotFound);
}
- (NSArray *)mostFrequentWordsInString:(NSString *)string count:(NSUInteger)count {
// получаем массив слов.
// такой подход для человеческих языков будет работать хорошо.
// для языков, вроде китайского, или когда язык заранее не известен,
// лучше использовать enumerateSubstringsInRange с опцией NSStringEnumerationByWords
NSMutableCharacterSet *separators = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
[separators formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
NSArray *words = [string componentsSeparatedByCharactersInSet:separators];
NSCountedSet *set = [NSCountedSet setWithArray:words];
// тут бы пригодился enumerateByCount, но его нет.
// будем строить вручную
NSMutableArray *selectedWords = [NSMutableArray arrayWithCapacity:count];
NSMutableArray *countsOfSelectedWords = [NSMutableArray arrayWithCapacity:count];
for (NSString *word in set) {
NSUInteger wordCount = [set countForObject:word];
NSNumber *countOfFirstSelectedWord = [countsOfSelectedWords count] ? [countsOfSelectedWords objectAtIndex:0] : nil; // в iOS 7 появился firstObject
if ([selectedWords count] < count || wordCount >= [countOfFirstSelectedWord unsignedLongValue]) {
NSNumber *wordCountNSNumber = [NSNumber numberWithUnsignedLong:wordCount];
NSRange range = NSMakeRange(0, [countsOfSelectedWords count]);
NSUInteger indexToInsert = [countsOfSelectedWords indexOfObject:wordCountNSNumber inSortedRange:range options:NSBinarySearchingInsertionIndex usingComparator:^(NSNumber *n1, NSNumber *n2) {
NSUInteger _n1 = [n1 unsignedLongValue];
NSUInteger _n2 = [n2 unsignedLongValue];
if (_n1 == _n2)
return NSOrderedSame;
else if (_n1 < _n2)
return NSOrderedAscending;
else
return NSOrderedDescending;
}];
[selectedWords insertObject:word atIndex:indexToInsert];
[countsOfSelectedWords insertObject:wordCountNSNumber atIndex:indexToInsert];
// если слов уже есть больше чем нужно, удаляем то что с наименьшим повторением
if ([selectedWords count] > count) {
[selectedWords removeObjectAtIndex:0];
[countsOfSelectedWords removeObjectAtIndex:0];
}
}
}
return [selectedWords copy];
// можно было бы сразу вернуть selectedWords,
// но возвращать вместо immutable класса его mutable сабклас нехорошо - может привести к багам
}
// очень интересный метод для юнитестов: правильный результат может быть разным и зависит от порядка слов в строке.
Я бы именно такой подход и использовал, если бы мне нужно было решить эту задачу в реальном iOS приложении, при условии, что я понимаю, откуда будут браться входные данные для поиска и предполагаю, что размеры входной строки не будут больше нескольких мегабайт. Вполне разумное допущение для iOS приложения, на мой взгляд. Иначе на входе не было бы строки, а был бы файл. При реально больших входных данных прийдется попотеть над регулярным выражением для перебора слов, чтоб избавиться от одного промежуточного массива. Такое регулярное выражение очень зависит от языка — то что сработает для русского не проканает для китайского. А вот что делать со словами дальше — кроме прямолинейного алгоритма в голову ничего не приходит. Если бы нужно было выбрать одно наиболее часто встречающееся слово — это Fast Majority Voting. Но вся красота этого алгоритма в том, что он работает для выбора одного значения. Модификаций алгоритма для выбора N значений мне не известны. Самому модифицировать не получилось.
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 1000; ++i) {
if ([operation isCancelled]) return;
process(data[i]);
}
}];
[queue addOperation:operation];
Лично я вижу проблему в том, что переменная operation, «захваченная» блоком при создании блока, еще не проинициализирована до конца. Какое реально значение этой переменной будет в момент «захвата», зависит от того, используется ли этот код в методе класса или в простой функции. Вполне возможно, что сгенерированный код будет вполне работоспособен и так, но мне этот код не ясен. Как выйти из ситуации? Так:
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
for (int i = 0; i < 1000; ++i) {
if ([operation isCancelled]) return;
process(data[i]);
}
}];
[queue addOperation:operation];
Возможно, достаточно было бы просто добавить модификатор __block
в объявление переменной operation. Но так, как в коде выше — наверняка. Некоторые даже создают __weak
копию переменной operation и внутри блока используют ее. Хорошо подумав я решил, что в данном конкретном случае, когда известно время жизни переменной operation и блока — это излишне. Ну и я бы подумал, стоит ли использовать NSBlockOperation
для таких сложных конструкций. Разумных доводов привести не могу — вопрос личных предпочтений.
Что еще с этим кодом не так? Я не люблю разных магических констант в коде и вместо 1000 использовал бы define
, const
, sizeof
или что-то в этом роде.
В длинных циклах в objective-c нужно помнить об autoreleased переменных и, если такие переменные используются в функции или методе process, а сам этот метод или функция об этом не заботится, нужно завернуть этот вызов в @autoreleasepool {}
. Создание нового пула при каждой итерации цикла может оказаться накладным или излишним. Если не используется ARC, можно создавать новый NSAutoreleasePool каждые, допустим, 10 итераций цикла. Если используется ARC, такой возможности нет. Кстати, это, наверное, единственный довод не использовать ARC.
По коду не ясно, меняются ли данные в процессе обработки, обращается ли кто-то еще к этим данным во время обработки из других потоков, какие используются структуры данных, заботится ли сам process о монопольном доступе к данным тогда когда это нужно. Может понадобиться позаботиться о блокировках.
Doh. Dear future googlers: of course operation is nil when copied by the block, but it doesn't have to be copied. It can be qualified with __block
like so:
//THIS MIGHT LEAK! See the update below.
__block NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while( ! [operation isCancelled]){
//do something...
}
}];
UPDATE:
Upon further meditation, it occurs to me that this will create a retain cycle under ARC. In ARC, I believe __block
storage is retained. If so, we're in trouble, because NSBlockOperation also keeps a strong references to the passed in block, which now has a strong reference to the operation, which has a strong reference to the passed in block, which…
It's a little less elegant, but using an explicit weak reference should break the cycle:
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation;
[operation addExecutionBlock:^{
while(![weakOperation isCancelled]){
//do something...
}
}];
NSString *str = @"a";
for (int i = 0; i< 5000000; i++) {
str = [str stringByAppendingString:@"a"];
}
Ответ
str =[@"" stringByPaddingToLength:5000000 withString:@"a" startingAtIndex:0];
Способ 1
// myString is "hi"
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
}
NSLog(@"%@", reversedString); // outputs "ih"
Способ 2
array.reverseObjectEnumerator.allObjects;
Способ 3
#include <string.h>
/* reverse: переворачивает строку s (результат в s) */
void reverse(char s[]) {
int c, i, j;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
с = s[i];
s[i] = s[j];
s[j] = c;
}
}
[[[SomeClass alloc] init] init];
Пример: есть класс A
, который имеет поле NSString *b
и в ините ты делаешь _b = @"somestring";
Стринг b
не хранится в памяти выделенной под A
– в этой памяти хранится лишь ссылка на b
, а сам объект создается вовне. При повторном ините стринг просто пересоздастся, не стерев старый, и мы получаем утекший стринг. Вообще, такая ситуация есть далеко не везде, и далеко не всегда вызовет проблемы. Но кастомные повторные инициализации реально могут вызывать утечки памяти — зависит от конкретного типа объекта. Двойной инит может вызвать утечку. А может не вызвать. Каждый конкретный класс - отдельный вопрос.
@interface A : NSObject
- (void)someMethod;
@end
@implementation A
- (void)someMethod {
NSLog(@"This is class A");
}
@end
@interface B : A
@end
@implementation B
- (void)someMethod {
NSLog(@"This is class B");
}
@end
@interface C : NSObject
@end
@implementation C
- (void)method {
A *a = [B new];
[a someMethod];
}
@end
Вызовется метод класса B.
2048 * 2024 = 4145152 байт, так как int8_t = 1 байт. Значит для хранения массива нужно 4.15 мегабайт памяти в стеке. На iOS размер стека ограничен 1 мегабайтом, следовательно, такая запись недопустима.
struct StructA {
int32_t a;
char b;
char c;
};
struct StructB {
char b;
int32_t a;
char c;
};
sizeof(StructA) == 8
sizeof(StructB) == 12
Как происходит выравнивание указателей на объекты в Objective-C?