Можно ли использовать Objective-C блоки как свойства?
Можно ли иметь блоки как свойства с использованием стандартного синтаксиса свойств?
Существуют ли какие-либо изменения для ARC?
Ответы
Ответ 1
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
Если вы собираетесь повторять один и тот же блок в нескольких местах, используйте тип def
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
Ответ 2
Вот пример того, как вы выполнили бы такую задачу:
#import <Foundation/Foundation.h>
typedef int (^IntBlock)();
@interface myobj : NSObject
{
IntBlock compare;
}
@property(readwrite, copy) IntBlock compare;
@end
@implementation myobj
@synthesize compare;
- (void)dealloc
{
// need to release the block since the property was declared copy. (for heap
// allocated blocks this prevents a potential leak, for compiler-optimized
// stack blocks it is a no-op)
// Note that for ARC, this is unnecessary, as with all properties, the memory management is handled for you.
[compare release];
[super dealloc];
}
@end
int main () {
@autoreleasepool {
myobj *ob = [[myobj alloc] init];
ob.compare = ^
{
return rand();
};
NSLog(@"%i", ob.compare());
// if not ARC
[ob release];
}
return 0;
}
Теперь единственное, что нужно изменить, если вам нужно изменить тип сравнения, будет typedef int (^IntBlock)()
. Если вам нужно передать два объекта, измените их на: typedef int (^IntBlock)(id, id)
и измените свой блок на:
^ (id obj1, id obj2)
{
return rand();
};
Надеюсь, это поможет.
EDIT 12 марта 2012 года:
Для ARC никаких конкретных изменений не требуется, поскольку ARC будет управлять блоками для вас, пока они определены как копия. Вам также не нужно устанавливать свойство nil в свой деструктор.
Подробнее читайте в этом документе:
http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Ответ 3
Для Swift просто используйте замыкания: пример.
В Objective-C ключом является:
@property (copy) void
@property (copy)void (^doStuff)(void);
Это так просто.
Документация Apple, которая на 100% объясняет эту проблему:
Apple Doco.
В вашем .h файле:
// Here is a block as a property:
//
// Someone passes you a block. You "hold on to it",
// while you do other stuff. Later, you use the block.
//
// The property 'doStuff' will hold the incoming block.
@property (copy)void (^doStuff)(void);
// Here a method in your class.
// When someone CALLS this method, they PASS IN a block of code,
// which they want to be performed after the method is finished.
-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater;
// We will hold on to that block of code in "doStuff".
Вот ваш файл .m:
-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater
{
// Regarding the incoming block of code, save it for later:
self.doStuff = pleaseDoMeLater;
// Now do other processing, which could follow various paths,
// involve delays, and so on. Then after everything:
[self _alldone];
}
-(void)_alldone
{
NSLog(@"Processing finished, running the completion block.");
// Here how to run the block:
if ( self.doStuff != nil )
self.doStuff();
}
Остерегайтесь устаревшего примера кода.
С современными (2014+) системами делайте то, что показано здесь. Это так просто.
Ответ 4
Для потомства/полноты... Вот два ПОЛНЫХ примера того, как реализовать этот смехотворно универсальный "способ делать вещи". Ответ @Robert блаженно кратким и правильным, но здесь я хочу также показать способы фактического "определения" блоков.
@interface ReusableClass : NSObject
@property (nonatomic,copy) CALayer*(^layerFromArray)(NSArray*);
@end
@implementation ResusableClass
static NSString const * privateScope = @"Touch my monkey.";
- (CALayer*(^)(NSArray*)) layerFromArray {
return ^CALayer*(NSArray* array){
CALayer *returnLayer = CALayer.layer
for (id thing in array) {
[returnLayer doSomethingCrazy];
[returnLayer setValue:privateScope
forKey:@"anticsAndShenanigans"];
}
return list;
};
}
@end
Глупый? Да. Полезно? Hells yeah. Вот другой, более атомный способ установки свойства.. и класс, который смехотворно полезен...
@interface CALayoutDelegator : NSObject
@property (nonatomic,strong) void(^layoutBlock)(CALayer*);
@end
@implementation CALayoutDelegator
- (id) init {
return self = super.init ?
[self setLayoutBlock: ^(CALayer*layer){
for (CALayer* sub in layer.sublayers)
[sub someDefaultLayoutRoutine];
}], self : nil;
}
- (void) layoutSublayersOfLayer:(CALayer*)layer {
self.layoutBlock ? self.layoutBlock(layer) : nil;
}
@end
Это иллюстрирует настройку свойства блока через аксессуар (хотя и внутри init, дискуссивно рискованная практика...) по сравнению с первым примером "неатомического" механизма "getter". В любом случае... "жестко закодированные" реализации всегда могут быть перезаписаны на один экземпляр.. a lá..
CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
[layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;
Также.. если вы хотите добавить свойство блока в категорию... скажите, что вы хотите использовать блок вместо какого-либо действия "целевое/действие" старой школы... Вы можете просто использовать связанные значения, ну.. свяжите блоки.
typedef void(^NSControlActionBlock)(NSControl*);
@interface NSControl (ActionBlocks)
@property (copy) NSControlActionBlock actionBlock; @end
@implementation NSControl (ActionBlocks)
- (NSControlActionBlock) actionBlock {
// use the "getter" method selector to store/retrieve the block!
return objc_getAssociatedObject(self, _cmd);
}
- (void) setActionBlock:(NSControlActionBlock)ab {
objc_setAssociatedObject( // save (copy) the block associatively, as categories can't synthesize Ivars.
self, @selector(actionBlock),ab ,OBJC_ASSOCIATION_COPY);
self.target = self; // set self as target (where you call the block)
self.action = @selector(doItYourself); // this is where it called.
}
- (void) doItYourself {
if (self.actionBlock && self.target == self) self.actionBlock(self);
}
@end
Теперь, когда вы делаете кнопку, вам не нужно настраивать какую-либо драму IBAction
.. Просто сопоставьте работу, которую нужно выполнить при создании...
_button.actionBlock = ^(NSControl*thisButton){
[doc open]; [thisButton setEnabled:NO];
};
Этот шаблон может применяться OVER и OVER до Cocoa API. Используйте свойства, чтобы сблизить соответствующие части вашего кода, устранить запутанные парадигмы делегирования и использовать силу объектов, помимо того, что они действуют как немые "контейнеры".
Ответ 5
Конечно, вы можете использовать блоки как свойства. Но убедитесь, что они объявлены как @property (копия). Например:
typedef void(^TestBlock)(void);
@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end
В MRC блоки захвата переменных контекста выделяются в стек; они будут выпущены, когда рама стека будет уничтожена. Если они скопированы, новый блок будет выделен в куче, который может быть выполнен позже после того, как будет вставлен фрейм стека.
Ответ 6
Юридическая информация
Это не предназначено для "хорошего ответа", так как этот вопрос задается явно для ObjectiveC. Поскольку Apple представила Swift на WWDC14, я хотел бы поделиться различными способами использования блока (или закрытия) в Swift.
Привет, Swift
У вас есть много способов предложить блок, эквивалентный функции в Swift.
Я нашел три.
Чтобы понять это, я предлагаю вам испытать на детской площадке этот маленький кусок кода.
func test(function:String -> String) -> String
{
return function("test")
}
func funcStyle(s:String) -> String
{
return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle)
let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle)
let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" })
println(resultFunc)
println(resultBlock)
println(resultAnon)
Swift, оптимизированный для закрытия
Поскольку Swift оптимизирован для асинхронной разработки, Apple больше работала над закрытием.
Первая заключается в том, что сигнатура функции может быть выведена, поэтому вам не нужно ее переписывать.
Доступ к параметрам по номерам
let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })
Вывод Params с именованием
let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
Закрытие закрытия
Этот специальный случай работает, только если блок является последним аргументом, он называется закрывающим закрытием
Вот пример (объединенный с выведенной сигнатурой для отображения мощности Swift)
let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }
Наконец:
Используя всю эту силу, я сделаю смешение закрывающего закрытия и ввода типа (с именованием для удобочитаемости)
PFFacebookUtils.logInWithPermissions(permissions) {
user, error in
if (!user) {
println("Uh oh. The user cancelled the Facebook login.")
} else if (user.isNew) {
println("User signed up and logged in through Facebook!")
} else {
println("User logged in through Facebook!")
}
}
Ответ 7
Привет, Swift
В дополнение к тому, что ответил @Francescu.
Добавление дополнительных параметров:
func test(function:String -> String, param1:String, param2:String) -> String
{
return function("test"+param1 + param2)
}
func funcStyle(s:String) -> String
{
return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle, "parameter 1", "parameter 2")
let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle, "parameter 1", "parameter 2")
let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" }, "parameter 1", "parameter 2")
println(resultFunc)
println(resultBlock)
println(resultAnon)
Ответ 8
Вы можете следить за форматом ниже и можете использовать свойство testingObjectiveCBlock
в классе.
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end
Для получения дополнительной информации смотрите здесь