【问题标题】:Please help me with block-based callbacks请帮助我处理基于块的回调
【发布时间】:2023-03-22 11:35:01
【问题描述】:

我对基于块的回调有一个误解。我似乎知道有两种方法,我不知道什么时候应该使用另一种,所以有人可以向我解释这两种方法之间的区别,纠正我并在需要时给我一些提示任何。

我从 stackoverflow 以及其他地方的库中找到了一些代码,因此感谢编写此代码的人。

typedef void (^MyClickedIndexBlock)(NSInteger index);
@interface YourInterface : YourSuperClass
@property (nonatomic, strong) MyClickedIndexBlock clickedIndexBlock

.m
//where you have to call the block
if (self.clickedIndexBlock != nil) {self.clickedIndexBlock(buttonIndex)};

// where you want to receive the callback
alert.clickedIndexBlock = ^(NSInteger index){NSLog(@"%d", index);};

我对上面的理解是:

  1. MyClickedIndexBlock 是 NSInteger 的 typedef。以 MyClickedIndexBlock 类型的名称“clickedIndexBlock”创建的属性(意味着 clickedIndexBlock 可以是一个数字)。

  2. 块也可以用作方法,这就是为什么我可以调用 self.clickedIndexBlock(buttonIndex);

但是有些东西告诉我,这种作为@property 的方法只真正支持一个参数 例如。 NS 整数。

鉴于以下方法允许使用多个参数。

bluetoothMe.h

typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);

- (void)hardwareResponse:(hardwareStatusBlock)block;

bluetoothMe.m

- (void)hardwareResponse:(hardwareStatusBlock)block {
privateBlock = [block copy]; 
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Did connect to peripheral: %@", peripheral);

privateBlock(peripheral, BLUETOOTH_STATUS_CONNECTED, nil);

NSLog(@"Connected");
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
 }

我的理解是创建一个强大的属性并执行 [块复制] 将保留该块直到应用程序终止。所以 [block copy] 和 strong 都保留。 [块复制] 应用于块以保留,否则当方法超出范围时块将消失。

ViewController.m

[instance hardwareResponse:^(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error) {

    if (status == BLUETOOTH_STATUS_CONNECTED)
    {
        NSLog(@"connected!");
    }
    else if (status == BLUETOOTH_STATUS_FAIL_TO_CONNECT)
    {
        NSLog(@"fail to connect!");
    }
    else
    {
        NSLog(@"disconnected!");
    }

    NSLog(@"CBUUID: %@, ERROR: %@", (NSString *)peripheral.UUID, error.localizedDescription);
}];

那么让我们看看我的问题是什么:

1) 我什么时候会选择第一种方法而不是第二种方法,反之亦然?

2) 第一个示例,块是属性的 typedef。第二个例子,块被声明为一个方法。为什么第一个例子不能声明一个方法,为什么第二个例子不能 typedef 到一个属性?

3) 我是否需要为我想要基于块的回调的每种委托方法创建一个 typedef?

4) 迄今为止,我只看到支持一种委托方法。如果我要在多个不相似的委托方法上创建基于块的回调,您能否向我展示一个如何实现每种方法的示例。

感谢您的反馈。这有时很难。需要尽可能多的帮助。 谢谢,

【问题讨论】:

  • 一个问题太多了……

标签: ios objective-c ios5 ios6 objective-c-blocks


【解决方案1】:

问题

  • 不管typedef一个块与否,
  • 是否将属性用于块,
  • 一个块是否有一个或多个参数,

完全独立的(或正交)。所有组合都是 可能和允许。

void (^myClickedIndexBlock)(NSInteger index);

声明一个块变量myClickedIndexBlock接受一个整数参数 并返回无效。如果出现相同的块类型,您可以使用typedef 在你的程序中反复出现:

// Define MyClickedIndexBlock as *type* of a block taking an integer argument and returning void:
typedef void (^MyClickedIndexBlock)(NSInteger index);
// Declare myClickedIndexBlock as a *variable* of that type:
MyClickedIndexBlock myClickedIndexBlock;

有多个参数:

void (^privateBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);

typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
hardwareStatusBlock privateBlock;

您可以使用属性来代替(实例)变量。在第一个例子中:

@property (nonatomic, copy) void (^myClickedIndexBlock)(NSInteger index);

myClickedIndexBlock声明为块属性,相当于

typedef void (^MyClickedIndexBlock)(NSInteger index);
@property (nonatomic, copy) MyClickedIndexBlock clickedIndexBlock;

与您的假设相反,块属性不限于块 用一个论点。您也可以在第二个示例中使用属性, 有无 typedef:

@property (nonatomic, copy) void (^privateBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);

typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
@property (nonatomic, copy) privateBlock;

您可以选择是否为块使用实例变量或属性。 我会使用属性(带有“复制”属性)。

是否使用 typedef 纯粹是个人喜好问题。它有助于避免 如果您的程序中重复出现相同的块类型,则会出错。在另一 手,Xcode 自动补全似乎在没有 typedef 的情况下工作得更好(在我的 经验)。

【讨论】:

  • 只是为了学究气,我想补充一点关于块如何接受 VARARGS 和其他块。
  • Martin,你证明了typedef void (^MyClickedIndexBlock)(NSInteger index); will create a block variable. So what does this line MyClickedIndexBlock myClickedIndexBlock;` 做吗?
  • @Ben:typedef void (^MyClickedIndexBlock)(NSInteger index); 声明了 块类型 MyClickedIndexBlockMyClickedIndexBlock myClickedIndexBlock; 声明了具有该类型的 变量 myClickedIndexBlock。例如 typedef unsigned long long ull; ull x; 声明 type ull 然后声明一个 variable x 具有此类型。
  • @Ben:比较 stackoverflow.com/a/15311069/1187415 和该答案中的链接,了解声明块类型和变量的语法。
  • @CodaFi:感谢您的反馈。我试图回答这个问题的 3 个不同方面,但又不会让答案过于复杂,因此我宁愿不给它增加更多复杂性。
【解决方案2】:

我强烈建议您阅读Blocks Programming Guide

块不是方法。我不打算解释Conceptual Overview 中所说的内容,而只是引用一些部分:

块通常代表小的、独立的代码片段。 [...]
它们允许您在调用点编写代码,稍后在方法实现的上下文中执行。

您似乎对语法感到困惑。

typedef void (^MyClickedIndexBlock)(NSInteger index);

它基本上只是定义了一个名为 MyClickedIndexBlock 的类型,表示一个块,该块接受一个 NSInteger 类型的参数并且不返回任何内容 (void)。
不是对 NSInteger 的 typedef。

@property (nonatomic, strong) MyClickedIndexBlock clickedIndexBlock

是一个包含 MyClickedIndexBlock 的属性的声明。
不需要 typedef 块,编写完全有效

@property (nonatomic, strong) void(^clickedIndexBlock)(NSInteger index);

但为了清楚起见(或重用),您可以选择对它们进行 typedef。请注意,属性名称是 ^ 之后的名称。

您说块可以用作方法,因为在您的示例中可以调用self.clickedIndexBlock(buttonIndex)。但实际上,正是因为你声明了一个名为clickedIndexBlock的属性,你才可以这样称呼它。

您的问题有很多,但很大一部分是由于混淆和误解。您提到的两种方法并没有真正的不同。块是对象,可以像处理 NSString 或其他类型的对象一样作为参数、局部变量或 ivars / 属性进行操作。

【讨论】:

  • 在块的特殊情况下使用 strong 和 copy 有什么区别? ARC会自动添加“副本”吗?在这种情况下,强有意义吗?哪个更受欢迎?
  • @Rivera ARC 在这种情况下会自动复制区块。你也可以使用@property (nonatomic, copy),它可以在 ARC 和 MRC 下工作。
【解决方案3】:

1) 块不是类型定义为整数。它是 typedef 的返回 void 并且有一个整数参数。精神 1 或方法 2 没有优势;如果声明,它们都可以有多个参数。

2) 没有理由为这两种情况选择该格式。它们都达到了相同的结果,但第一个在语义上可以说更好。

3) 不可以。您可以将块内联声明到方法中。查看 [NSArray enumerateObjectsUsingBlock:] 的标头以获取内联块声明的示例。

4) 您可以创建多个属性并在必要时调用每个不同的块。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    相关资源
    最近更新 更多