Strange “zombie” in forwardInvocation: + getArgument:atIndex methods

自闭症网瘾萝莉.ら 提交于 2019-12-10 22:09:59

问题


Here is part from my code:

- (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];
    //    }];
}

and here is the part which processes the userGetWithCustomOptions: method:

- (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];
}

problem is that when I am using "options" variable - it works fine, but when using direct values - it fails, app crashes. Using Profile I have found that method call directs to deallocated object.

Why this happens? There is no other code that can help.

ViewController.m code: https://gist.github.com/AndrewShmig/5398546

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


回答1:


The problem is that the parameter of getArgument: is type void *. And you are passing &value, which is NSDictionary * __strong * (pointer to a strong reference) to it. The cast is valid because it is possible to assign any non-object pointer to and from void * without any warnings.

When you pass a "pointer to strong" to a function, that means the function should expect the pointer to a "strong reference", and when the function exits, it should preserve the fact that the pointer points to a "strong reference". What this means is that if the function changes the reference (pointed to by the pointer), it must first release the previous value and then retain the new value.

However, what does getArgument:atIndex: do with its void * argument? It is agnostic about the thing pointed to, and simply copies the value into the memory pointed to. Therefore, it does not do any of this retain and release stuff. Basically, it performs a plain-old pre-ARC non-retaining assignment into your value variable.

So why is it crashing? What is happening is that value is at first nil, and then inside the getArgument:atIndex:, it assigns the new value into it, but it does not retain it. However, ARC assumes that it has been retained, since value is a strong reference. So at the end of the scope, ARC releases it. This is an over-release, since it was never retained.

The solution is to not pass a "pointer to strong" into getArgument:, because that method does not know anything about "strong". Instead, pass a "pointer to unsafe_unretained" or "pointer to void" into it, and then convert it to a strong reference later:

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

or alternately:

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


来源:https://stackoverflow.com/questions/16044817/strange-zombie-in-forwardinvocation-getargumentatindex-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!