问题
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