Objective-C эквивалентен анонимным классам Java в методах классов
Я хочу установить делегат объекта внутри метода класса в Objective-C. Псевдо-код:
+ (ClassWithDelegate*) myStaticMethod {
if (myObject == nil) {
myObject = [[ClassWithDelegate alloc] init];
// myObject.delegate = ?
}
return myObject;
}
В Java я просто создал бы анонимный класс, который реализовывал протокол делегата. Как я могу сделать нечто подобное в Objective-C?
В основном я хотел бы избежать создания отдельного класса (и файлов) для реализации простого протокола делегирования.
3 ответа
В настоящее время в Objective-C нет анонимных классов.
Часто вы можете использовать уже существующий объект. Например, для NSTableViewDataSource вы можете реализовать методы в документе или в контроллере представления и передать их в качестве делегата.
Или вы можете заставить сам объект реализовать протокол и сделать его своим собственным делегатом в случае по умолчанию.
Или методы, которые отправляют сообщения делегата, могут проверять наличие нулевого делегата и делать что-то разумное в этой ситуации.
Или вы можете объявить и определить класс внутри файла реализации, в котором вы создаете объект, которому нужен делегат.
Как справедливо сказал JeremyP, в Objective C нет анонимных классов, как в Java.
Но в Java анонимные классы в основном используются для реализации интерфейса с одним методом или того, что мы также называем функциональным интерфейсом.
Мы делаем это, чтобы избежать необходимости реализовывать интерфейс ** в классе ** только для реализации одного метода, который чаще всего используется для прослушивателей, наблюдателей и обработчиков событий.
Это в основном делается из-за ** отсутствия анонимных функций первого класса в Java (до версии 8 и лямбда-проекта).
В Objective C есть нечто, называемое блоками, где вы можете напрямую передать блок, содержащий реализацию этого единственного метода, а не пустой класс, обертывающий его.
Пример:
Использование анонимного класса в Java
//Functional interface
interface SomethingHandler
{
void handle(Object argument);
}
//a method that accepts the handler in some other class
class SomeOtherClass
{
void doSomethingWithCompletionHandler(SomethingHandler h){
// do the work that may consume some time in a separate thread may be.
// when work is done call the handler with the result which could be any object
h.handler(result);
};
}
// Somewhere else in some other class, in some other code
// passing the handler after instantiating someObj as an object of SomeOtherClass which can use the handler as needed
SomeOtherClass someObj = new SomeOtherClass();
someObj.doSomethingWithCompletionHandler( new SomethingHandler()
{
void handle(Object argument)
{
// handle the event using the argument
}
});
В цели C
// declare the handler block
typedef void (^SomethingHandler)(id argument){}
// this interface is different than Java interface which are similar to Protocols
@interface SomeOtherClass
-(void)doSomethingWithCompletionHandler:(SomethingHandler)h;
@end
@implementation SomeOtherClass
-(void)doSomethingWithCompletionHandler:(SomethingHandler)h
{
// do the work that may consume some time in a separate thread may be.
// when work is done call the handler with the result which could be any object
h(result);
}
@end
// passing the handler after instantiating someObj as an object of SomeOtherClass which can use the handler as needed
SomeOtherClass* someObj = [[SomeOtherClass alloc] init]; // ARC :)
[someObj doSomethingWithCompletionHandler:^(id argument)
{
// handle the event using the argument
}];
Анонимные классы могут быть реализованы с помощью библиотеки. Несколько месяцев назад я работал над MMMutableMethods
fork для улучшения старой реализации (обсуждается с автором) и добавления моего собственного механизма без каких-либо манипуляций во время выполнения obj-c.
https://github.com/k06a/MMMutableMethods
A. Первый механизм работает над созданием класса времени выполнения obj-c:
MM_CREATE(MM_REUSE,^(Class class){
[class addMethod:@selector(onResultWithId:)
fromProtocol:@protocol(AMCommandCallback)
blockImp:^(id this,id res){
NSLog(@"onResultWithId: %@",res);
}];
[class addMethod:@selector(onErrorWithJavaLangException:)
fromProtocol:@protocol(AMCommandCallback)
blockImp:^(id this,JavaLangException *e){
NSLog(@"onErrorWithJavaLangException: %@",e);
}];
})
Б. Второй механизм работает над простой реализацией пересылки сообщений:
MM_ANON(^(MMAnonymousClass *anon){
[anon addMethod:@selector(onResultWithId:)
fromProtocol:@protocol(AMCommandCallback)
blockImp:^(id this,id res){
NSLog(@"onResultWithId: %@",res);
}];
[anon addMethod:@selector(onErrorWithJavaLangException:)
fromProtocol:@protocol(AMCommandCallback)
blockImp:^(id this,JavaLangException *e){
NSLog(@"onErrorWithJavaLangException: %@",e);
}];
})
Сначала создаются новые классы obc-j во время выполнения, это позволяет создавать классы MM_CREATE_CLASS(MM_REUSE, *)
и непосредственно случаи с MM_CREATE(MM_REUSE, *)
, Классы будут создаваться только при первом выполнении и повторно использоваться по умолчанию, но вы можете избежать повторного использования, вызвав MM_CREATE_CLASS_ALWAYS(*)
а также MM_CREATE_ALWAYS(*)
,
Второй механизм не создает никаких экземпляров времени выполнения, просто запоминает блоки для селекторов и перенаправляет вызовы методов к ним.
Я предпочитаю второй способ не создавать много классов во время выполнения. ИМХО это намного безопаснее и достаточно мощно.
Чтобы использовать эту библиотеку просто:
pod 'MMMutableMethods', :git => 'https://github.com/k06a/MMMutableMethods'