iOS - 直接设置阻止块不起作用

时间:2015-05-08 21:49:10

标签: ios objective-c-blocks

标题上写着,我想问为什么以下不起作用,因为它应该是imho。

// ViewController.m
#import "B.h"
...

@implementation ViewController
{
    B *bInstance;
}

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
    {
        bInstance = [[B alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
        [bInstance setBlockCalled:^(NSDictionary *dict) {
            NSLog(@"%@", dict[@"key"]);
        }];

        [self.view addSubview:bInstance];
    }
    return self;
}


// B.h
#import <UIKit/UIKit.h>

@interface B : UIView
@property (nonatomic, copy) void (^blockCalled)(NSDictionary *);
@end

// B.m
#import "B.h"
#import "A.h"

@implementation B
{
    A *aInstance;
    void (^blockCalled)(NSDictionary *);
}

@synthesize blockCalled;

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        aInstance = [[A alloc] initWithFrame:frame];
        [aInstance setBlockCalled:blockCalled];

        [self addSubview:aInstance];
    }
    return self;
}

@end


// A.h
#import <UIKit/UIKit.h>

@interface A : UIView

@property (nonatomic, copy) void (^blockCalled)(NSDictionary *);

@end

// A.m
#import "A.h"

@implementation A
{
    void (^blockCalled)(NSDictionary *);
}

@synthesize blockCalled;

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setFrame:CGRectMake(0, 0, 100, 100)];
        [button setBackgroundColor:[UIColor redColor]];
        [button addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];

        [self addSubview:button];
    }
    return self;
}

- (void) buttonClicked
{
    blockCalled(@{@"key":@"value"});
}

@end

我想要做的是&#39;遍历视图hiearchy&#39;,就我所见,我指定一个具有相同参数的块变量,所以我&#39; d期待它的运作。为什么这是一个错误的想法?

编辑:在发生此问题时添加了更完整的示例。

EDIT2:添加了MCVE,我已经测试过。

在我检查了MCVE后,代码在A.m中的blockCalled(@{@"key":@"value"});行崩溃,因为blockCalled为零。

更新了问题:我想知道为什么呼叫[aInstance setBlockCalled:blockCalled]并未设置A中的blockCalled,因为在我看来,它与< / p>

[aInstance setBlockCalled:^(NSDictionary *dict) 
{ 
    __strong typeof (self) strongSelf = self; 
    strongSelf.blockCalled(dict); 
}];

2 个答案:

答案 0 :(得分:1)

@implementation ViewController
{
    B *bInstance;
}

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
    {
        bInstance = [[B alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
        [bInstance setBlockCalled:^(NSDictionary *dict) {
            NSLog(@"%@", dict[@"key"]);
        }];

        [self.view addSubview:bInstance];
    }

在初始化bInstance时,您还没有设置阻止。这意味着

aInstance = [[A alloc] initWithFrame:frame];
[aInstance setBlockCalled:blockCalled];

之前调用

[bInstance setBlockCalled:^(NSDictionary *dict) {
    NSLog(@"%@", dict[@"key"]);
}];

您应该覆盖B中的块设置器并在A上调用它。

// B.m
-(void)setBlockCalled(void(^)(NSDictionary*))passedBlock{
      [a setBlockCalled:passedBlock];
}

答案 1 :(得分:0)

问题(a)中的代码使用了不必要的ivars和@synthesize语句; (b)此代码段不足以重现您描述的崩溃。

话虽如此,问题中的代码示例提出了两种可能的崩溃源,即:(a)如果A被释放,代码无法删除观察者; (b)在试图打电话之前,确实应该仔细检查以确保这些街区是非零的。

但是,请考虑以下事项:

// A.h

@interface A : UIView
@property (nonatomic, copy) void (^blockCalled)(NSDictionary *dict);
@end

// A.m

@implementation A

- (instancetype) initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"kNotification" object:nil];
    }
    return self;
}

- (void)handleNotification:(NSNotification *)notification {
    // never just call `blockCalled`; always check to see if not null

    if (self.blockCalled) {
        // everything is good, so let's call the block

        self.blockCalled(notification.userInfo);
    }
}

- (void)dealloc {
    // never just `addObserver`; make sure to remove the observer when this is deallocated

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"kNotification" object:nil];
}

@end

// B.h

@interface B : UIView
@property (nonatomic, copy) void (^blockCalled)(NSDictionary *dict);
@property (nonatomic, strong) A *aInstance;
@end

// B.m
@implementation B

- (void) someMethod {
    // !!!!! why this crashes the app when blockCalled on aInstance is called:
    [self.aInstance setBlockCalled:self.blockCalled];

    // but this does not crash when the same happens
    __weak typeof (self) weakSelf = self;

    self.aInstance.blockCalled = ^(NSDictionary *dict) {
        __strong typeof (self) strongSelf = weakSelf;

        // again, never just call `blockCalled`; always check to see if not null

        if (strongSelf.blockCalled) {
            strongSelf.blockCalled(dict);
        }
    };
}

@end

最重要的是,当我修复两个明显的崩溃源时(未能删除观察者并且无法检查以确保块在调用之前是非零),并使用逻辑方案测试它,它似乎工作正常:

- (void)viewDidLoad {
    [super viewDidLoad];

    B *b = [[B alloc] init];
    b.blockCalled = ^(NSDictionary *dict) {
        NSLog(@"%@", dict);
    };
    A *a = [[A alloc] init];
    b.aInstance = a;
    [b someMethod];

    [self.view addSubview:a];
    [self.view addSubview:b];

    NSDictionary *dict = @{@"foo": @"bar", @"baz": @"qux"};
    [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotification" object:nil userInfo:dict];
}

因此,假设这两个问题中的一个不是问题的根源,您必须提供MCVE我们可以用来重现您描述的崩溃。