这是我的代码的一部分:
- (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/