objective-c - forwardInvocation : + getArgument:atIndex methods 中出现奇怪的 "zombie"

标签 objective-c automatic-ref-counting objective-c-blocks nszombie nsinvocation

这是我的代码的一部分:

- (void)viewDidLoad
{
    [super viewDidLoad];

    CGRect frame = [[UIScreen mainScreen] bounds];
    _webView = [[UIWebView alloc] initWithFrame:frame];
    [_webView setHidden:NO];
    [self.view addSubview:_webView];

    _vk = [[DPVkontakteCommunicator alloc] initWithWebView:_webView];

    DPVkontakteUserAccount *user;
    NSString *accessToken = [[NSUserDefaults standardUserDefaults]
                                             objectForKey:@"accessToken"];
    NSInteger userId = [[[NSUserDefaults standardUserDefaults]
                                         objectForKey:@"userId"] integerValue];
    user = [[DPVkontakteUserAccount alloc]
                                    initUserAccountWithAccessToken:accessToken
                                                            userId:userId];

    NSLog(@"%@", user);

    [user setSuccessBlock:^(NSDictionary *dictionary)
    {
        NSLog(@"%@", dictionary);
    }];

    NSDictionary *options = @{@"uid":@"1"};
    //    [user usersGetWithCustomOptions:@{@"uid":@"1"}]; // Zombie
    [user usersGetWithCustomOptions:options]; // Not zombie

    //    __block NSDictionary *options = @{};
    //
    //    [_vk startOnCancelBlock:^{
    //        NSLog(@"Cancel");
    //    } onErrorBlock:^(NSError *error) {
    //        NSLog(@"Error: %@", error);
    //    } onSuccessBlock:^(DPVkontakteUserAccount *account) {
    //        NSLog(@"account:%@", account);
    //
    //        [account setSuccessBlock:^(NSDictionary *dictionary)
    //        {
    //            NSLog(@"%@", dictionary);
    //        }];
    //
    //        [account docsGetUploadServerWithCustomOptions:options];
    //    }];
}

这里是处理 userGetWithCustomOptions: 方法的部分:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSString *methodName = NSStringFromSelector([anInvocation selector]);
    NSDictionary *options;

    [anInvocation getArgument:&options
                      atIndex:2];

    NSArray *parts = [self parseMethodName:methodName];
    NSString *vkURLMethodSignature = [NSString stringWithFormat:@"%@%@.%@",
                                                                kVKONTAKTE_API_URL,
                                                                parts[0],
                                                                parts[1]];
    // appending params to URL
    NSMutableString *fullRequestURL = [vkURLMethodSignature mutableCopy];

    [fullRequestURL appendString:@"?"];

    [options enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
    {
        [fullRequestURL appendFormat:@"%@=%@&", key, [obj encodeURL]];
    }];

    [fullRequestURL appendFormat:@"access_token=%@", _accessToken];

    // performing HTTP GET request to vkURLMethodSignature URL
    NSURL *url = [NSURL URLWithString:fullRequestURL];
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
    AFJSONRequestOperation *operation;
    operation = [AFJSONRequestOperation
            JSONRequestOperationWithRequest:urlRequest
                                    success:^(NSURLRequest *request,
                                              NSHTTPURLResponse *response,
                                              id JSON)
                                    {
                                        _successBlock(JSON);
                                    }
                                    failure:^(NSURLRequest *request,
                                              NSHTTPURLResponse *response,
                                              NSError *error,
                                              id JSON)
                                    {
                                        _errorBlock(error);
                                    }];

    [operation start];
}

问题是,当我使用“选项”变量时 - 它工作正常,但当使用直接值时 - 它失败,应用程序崩溃。使用配置文件我发现方法调用指向释放的对象。

为什么会这样? 没有其他代码可以提供帮助。

ViewController.m 代码:​​https://gist.github.com/AndrewShmig/5398546

DPVkontakteUserAccount.m:https://gist.github.com/AndrewShmig/5398557

最佳答案

问题是 getArgument: 的参数是 void * 类型。你正在传递 &value,它是 NSDictionary * __strong *(指向强引用的指针)。转换是有效的,因为可以在没有任何警告的情况下将任何非对象指针分配给 void * 或从中分配任何非对象指针。

当您将“指向强引用”的指针传递给函数时,这意味着函数应该期望指针指向“强引用”,并且当函数退出时,它应该保留指针指向“强引用”的事实引用”。这意味着如果函数更改引用(指针指向),它必须先释放以前的值,然后保留新值。

但是,getArgument:atIndex: 用它的 void * 参数做了什么?它对指向的东西是不可知的,只是将值复制到指向的内存中。因此,它不会做任何保留和释放的事情。基本上,它对您的 value 变量执行一个普通的 pre-ARC 非保留赋值。

为什么会崩溃?发生的事情是 value 首先是 nil,然后在 getArgument:atIndex: 中,它将新值分配给它,但是它不保留它。但是,ARC 假定它已被保留,因为 value 是一个强引用。所以在范围结束时,ARC 将其释放。这是一个过度发布,因为它从未被保留。

解决方案是不要将“指向强的指针”传递给 getArgument:,因为该方法对“强”一无所知。相反,将“指向 unsafe_unretained 的指针”或“指向 void 的指针”传递给它,然后再将其转换为强引用:

NSDictionary * __unsafe_unretained temp;
[anInvocation getArgument:&temp atIndex:2];
NSDictionary *options = temp; // or you can just use temp directly if careful

或者:

void *temp;
[anInvocation getArgument:&temp atIndex:2];
NSDictionary *options = (__bridge NSDictionary *)temp;

关于objective-c - forwardInvocation : + getArgument:atIndex methods 中出现奇怪的 "zombie",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16044817/

相关文章:

ios - 为什么 __weak 对象会被添加到自动释放池中?

ios - __weak + __block 可以同时使用一个变量吗

iphone - 如何从完成 block 中检索返回值?

ios - 如何定位 block 代码

ios - 负片/深色图像,源文件看起来不错。 iOS

objective-c - 为什么 Objective-C 中的 "self"可以同时接收 "class method"和 "instance method"?

ios - 在 ARC 下处理容器中 CGImageRef 的正确方法

ios - 使dispatch_async内的NSTimer失效

iOS - 处理来自 NSUserDefaults 的全局应用程序变量

ios - AutoLayout 和 AVCaptureVideoPreviewLayer