Pull to refresh

Comments 7

Сам имею аналогичное решение, похожее на анонимные классы в джаве:

tableView.datasource = [ASPDelegate delegate:^(ASPDynamicDelegate* delegate){

[delegate addSelector:@selector(tableView:numberOfRowsInSection:) withBlock:^NSUInteger(id _s, NSTableView *tv, NSUInteger section){
return 10;
}]

}];


И только для того, чтобы избавиться от id _s, вы решили прибегнуть к такому огромному количеству приседаний и использованию NSInvocation? Why?
Потому что намного приятнее работать только с тем, что нужно

А поскольку:
Это был всего лишь эксперимент, у меня не было желания написать этот код для использования в проектах.

и это действительно было увлекательное занятие и я думаю все объясняет ;-)
К тому же адаптация кол-ва аргументов тоже весьма приятно
Принято :) Не заметил.

Кстати, у Вас в структуре block ABI ошибка, на самом деле она выглядит вот так
struct ASPBlockDescriptor
{
__unused unsigned long int reserved;
__unused unsigned long int size;
union
{
struct
{
__unused void (*copy_helper)(void *dst, void *src);
__unused void (*dispose_helper)(void *src);
const char *copy_dispose_signature;
};

const char *usual_signature;
};
};

Документация is out of date, информацию о реальной имплементации можно найти в исходниках llvm. Разница не принципиальная, и так и так работает, но просто на заметку.

Попробуйте фана ради решить такую задачу: сделать возможным замену методов у конкретного инстанса класса с вызовом оригинальной имплементации перед\до. Например

[object injectAfter:@selector(someMethod:) withBlock:^(id _s, ArgType arg){}];


Блок должен быть вызван в этом примере после отработки оригинальной имплементации и при этом с теми же аргументами. Решение готовое уже есть на гитхабе, можете поискать, но оно использует NSInvocation и, по сему, невероятно медленное (о чем говорит их дока и мой опыт). Однако инструмент очень полезен: можно, например, очень быстро распихать аналитику по всему приложению, не вставляя злосчастные вызовы логинга эвентов посреди бизнес логики или UI.

Нужно сделать без использования NSInvocation. Эта задача мне кажется интересней и сложнее. Все здорово, покуда можно использовать NSInvocation. А вот без него? :) (PS: как видно в примере, _s не убирается и не требуется: у Вас узкий кейс с лямбдами, а эта задача о другом и self часто нужен)
Если честно я был в шоке. Мало того что очень странным путем пошли. Так еще и магию много раз вспомнили.
А как Вам решение в 2 строки?

_buttonOwner.strongPropertyForBlock = block
[button addTarget:block action:@selector(invoke) forControlEvents:UIControlEventTouchUpInside]

Главное держать блок столько же, сколько живет кнопка. Можно просто у владельца кнопки создать массив всех блоков. Можно даже к кнопке его приделать — это не хитрый метод, раз вы знакомы с рантайном, добавить проперти в системный класс — плёвое дело.

И вся статья сводится к:
1) Болк это NSObject, Наверно у него есть методы, вот давайте их и используем.
2) А если есть какие то контекстные переменные для блока, то создаем переменную, помечаем ее как __block и используем в блоке как хотим.

Я конечно тоже люблю пилить велосипед для изучения, и более глубоко понимания. Но показывая такое людям, можно дать неверное представление о природе вещей и в будущем вместо поисков короткого пути — пойдут знакомым, долгим и муторным.

Я сам не очень хороший писатель, даже отвратительный, я бы сказал. Но втыкать в центр статьи стороннюю инфу и так увеличивать статью — не хорошо.

P.S Не смотря на комент "When you call this method, target is not retained." у UIControl, даже если не держать ссылку на блок, все работает. Но я всегда храню ссылку на блок. Но раз живет на тестах. то давайте решение будет вообще однострочным.

P.P.S. Я не злой, но за 2 недели вижу такое решение 2ой раз. И считаю что такое на надо рассказывать. Сделали, заработало. Проктической пользы не несёт? Значит прячем в архивы на долгую память.

Вот вам распечатка интерфейса блока
@interface NSBlock: NSObject {
}

  • (id)copy;
  • (id)copyWithZone:(struct _NSZone { }*)arg1;
  • (void)invoke;
  • (void)performAfterDelay:(double)arg1;

end

приведите пример использования invoke, когда блок принимает аргументы
к примеру для вызова через performSelector:withObject:
ну или когда по нажатию на кнопку надо получить кнопку и евент

так же интересно, какое неверное представление я дал о блоках?
p.s. а я и не думал обижаться :-) это про "не злой"
"Неверно представление о блоках"
1) target-action на блоках, простенькая задача, но вы написали статью на пару А4, что создает впечатление о неподъемности вопроса
2) Если вы написали там много, и хотели показать всю взаимосвязь, то надо было еще добавить Такую цепочку логики — блок — это объект, объест это структура, простая Сишная структура с определенными правилами формирования. И тогда понятно зачем в статье декларируются странные структуры.
3) Слово магия недопустима в статьях, вообще. Как и мое многократное повторение слово структура :)

Параметры:
1) target-action ни есть задача о performSelector:withObject:
2) Если надо передать объект в блок как в performSelector:withObject:, просто используйте переменную блоке, и все счастливы будут
3) У меня на каждую пару эвент/кнопка всегда свой метод, и поэтому с моей "колокольни" это и не нужно. Но вот если прям очень-очень нужно, то

typedef void  (^ButtonExecutionBlock)(void);

@implementation UIButton (XYZ_SomeLabel)

- (void)XYZ_addBlock:(void (^)(id, UIControlEvents))block forEvent:(UIControlEvents)event
{
  weakify(self)
  ButtonExecutionBlock eventBlock = ^(void){
    strongify(self)
    block(self, event);
  };

  NSString *key = [NSString stringWithFormat:@"%lu", (unsigned long)event];
  const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];

  NSMutableArray *blocks = objc_getAssociatedObject(self, cKey);
  if (blocks == nil) {
    blocks = [NSMutableArray array];
    objc_setAssociatedObject(self, cKey, blocks, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  }
  [blocks addObject:eventBlock];

  [self addTarget:eventBlock
              action:@selector(invoke)
    forControlEvents:UIControlEventTouchUpInside];
}

@end

Да, тут нельзя удалить эвент, но тем не менее, допилить небольшая работа.
Да, ломается 2х строчное решение
Но это все равно меньше пары классов и статьи на пару А4. А по сути всего один метод у UIButton

Если бы Вы ограничили статью только темой "Разбираемся с NSMethodSignature на примере комбинирования stringWithFormat и NSArray" и убрали привязку к target-action — я бы лайкал статью неистово, всем одним лайком который у меня есть.
Если бы Вы ограничили статью только темой «Разбираемся с NSMethodSignature на примере комбинирования stringWithFormat и NSArray» и убрали привязку к target-action

Не поверите, было такое желание вынести центр статьи в отдельную статью с таким названием (и было так в начале кстати). Но потом люди которые читали черновой вариант изъявили желание читать все вместе чтобы не теряться в материале

Да, ломается 2х строчное решение
Но это все равно меньше пары классов и статьи на пару А4. А по сути всего один метод у UIButton

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

Здесь решение максимально общее и по факту совсем не большое если сравнивать от эффекта добавления множества категорий

1) target-action ни есть задача о performSelector:withObject:

Вы правы. Но суммарный результат, что решение применимо для любого действия где дергается через селектор вызов, я не смог подобрать лучшее определение
Sign up to leave a comment.

Articles