3

这里是我的代码部分:聊斋志异 “僵尸” 在forwardInvocation:+ getArgument:atIndex方法

- (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

+1

您可以加入至少剩余部分来自'DPVkontakeUserAccount'的'forwardInvocation:'方法,并将第一个代码示例放在上下文中 - 特别是在调用'usersGetWithCustomOptions:'之后特别引用了'options'变量?这可能会帮助民间人士帮助你。 – CRD

+0

@CRD现在可以吗? – AndrewShmig

+1

我相信这会帮助人们。你说“使用配置文件我发现方法调用指向释放对象。”,哪个方法调用?任何机会'enumerateKeysAndObjectsUsingBlock:'? – CRD

回答

3

的问题是,的getArgument:该参数的类型void *。并且您传递&value,即NSDictionary * __strong *(指向强引用)。该转换是有效的,因为可以在没有任何警告的情况下分配来自void *的任何非对象指针。

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

但是,getArgument:atIndex:做什么void *论点?对指向的事物是不可知的,并且简单地将该值复制到指向的内存中。因此,它不会做任何保留和释放的东西。基本上,它会在您的value变量中执行一个普通的ARC之前的非保留分配。

那么它为什么会崩溃?发生了什么事value最初是nil,然后在getArgument:atIndex:里面,它将新值赋给它,但它不保留它。但是,ARC假定它已被保留,因为value是一个有力的参考。因此,在范围的最后,ARC发布了它。这是一个过度释放,因为它从来没有保留。

解决方法是不要将“强指针”传递到getArgument:,因为该方法不知道任何有关“强”的内容。相反,通过一个“指针unsafe_unretained”或“指针无效”了进去,然后将其转换为强引用后:

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;