سؤال

I'm trying to initiate an NSObject subclass called FormObject in a JavascriptCore block. The FormObject is supposed to be nil until I set it in the JavascriptCore block. I need to set it in this block because After I show a UIActionSheet I save this FormObject in the UIActionSheetDelegate method - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex

Here is the code:

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (actionSheet.tag == kSaveFormConfirmationActionSheetTag) {
        if ([ButtonTitle isEqualToString:NSLocalizedString(@"Yes", @"Yes")]) {
            NSLog(@"Saving form data");
            NSLog(@"%@",formData);
            NSLog(@"%@",formData.dictionary);
            if (formData) {
                [[AutoFillManager defaultManager] saveFormData:formData];
                formData = nil;
            }
        }
        else if ([ButtonTitle isEqualToString:NSLocalizedString(@"Never for this Website", @"Never for this Website")]) {
            NSString *formURLString = [formData.urlString copy];
            formData = nil;
            formData = [[FormObject alloc] initWithDisabledSite:formURLString];
            [[AutoFillManager defaultManager] saveFormData:formData];
            formData = nil;

        }
        else {
            self.formData = nil;
        }
    }
}



- (JSContext *)getCurrentJavascriptContext {
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"JSTools" ofType:@"js"];
    NSString *scriptString = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
    [context evaluateScript:scriptString];
    context[@"print"] = ^(NSString *string) {
        NSLog(@"%@",string);
    };

    __weak AutoFillManager *afm = [AutoFillManager defaultManager];
    __weak UIToolbar *weakToolbar = toolbar;
    __weak typeof(self) weakSelf = self;

    context[@"receiveForm"] = ^(NSString *urlString, NSString *username, NSString *usernameFieldID, NSString *usernameFieldName, NSString *password, NSString *passwordFieldID, NSString *passwordFieldName) {

        NSURL *url = [NSURL URLWithString:urlString];

        for (NSDictionary *savedForm in [afm savedForms]) {
            FormObject *form = [[FormObject alloc] initWithFormDictionary:savedForm];
            if ([[url host] isEqualToString:form.urlString] && [username isEqualToString:form.username] && form.neverForThisSite == NO) {
#ifdef DEBUG
                NSLog(@"Site already saved");
#endif
                return;
            }

            if ([[url host] isEqualToString:form.urlString] && form.neverForThisSite == YES) {
#ifdef DEBUG
                NSLog(@"Never for this site");
#endif
                return;
            }
        }

        formData = [[FormObject alloc] initWithUsername:username withUsernameID:usernameFieldID withUsernameName:usernameFieldName withPassword:password withPasswordID:passwordFieldID withPasswordName:passwordFieldName withURLString:[url host]];
        UIActionSheet *saveFormConfirmationActionSheet = [[UIActionSheet alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Would you like to save this password?", @"Would you like to save this password?")] delegate:weakSelf cancelButtonTitle:NSLocalizedString(@"Not Now", @"Not Now") destructiveButtonTitle:nil otherButtonTitles:NSLocalizedString(@"Yes", @"Yes"), NSLocalizedString(@"Never for this Website", @"Never for this Website"), nil];
        saveFormConfirmationActionSheet.tag = kSaveFormConfirmationActionSheetTag;
        [saveFormConfirmationActionSheet showFromToolbar:weakToolbar];
    };

    return context;
}

I know the issue Is because of

formData = [[FormObject alloc] initWithUsername:username withUsernameID:usernameFieldID withUsernameName:usernameFieldName withPassword:password withPasswordID:passwordFieldID withPasswordName:passwordFieldName withURLString:[url host]];

If I add the specifier __weak Xcode will warn me to change it to __block. If I change it to __block then formData will still be nil afterwards.

How can I set the ivar in this block properly?

SOLUTION

The solution was to make the ivar a property and use weak self to set the property.

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    if ([ButtonTitle isEqualToString:NSLocalizedString(@"Never for this Website", @"Never for this Website")]) {
            NSString *formURLString = [self.formData.urlString copy];
            self.formData = nil;
            self.formData = [[FormObject alloc] initWithDisabledSite:formURLString];
            [[AutoFillManager defaultManager] saveFormData:self.formData];
            self.formData = nil;

        }
        else {
            self.formData = nil;
        }
    }
}


- (JSContext *)getCurrentJavascriptContext {
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"JSTools" ofType:@"js"];
    NSString *scriptString = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
    [context evaluateScript:scriptString];
    context[@"print"] = ^(NSString *string) {
        NSLog(@"%@",string);
    };

    __weak AutoFillManager *afm = [AutoFillManager defaultManager];
    __weak UIToolbar *weakToolbar = toolbar;
    __weak typeof(self) weakSelf = self;

    context[@"receiveForm"] = ^(NSString *urlString, NSString *username, NSString *usernameFieldID, NSString *usernameFieldName, NSString *password, NSString *passwordFieldID, NSString *passwordFieldName) {

        NSURL *url = [NSURL URLWithString:urlString];

        for (NSDictionary *savedForm in [afm savedForms]) {
            FormObject *form = [[FormObject alloc] initWithFormDictionary:savedForm];
            if ([[url host] isEqualToString:form.urlString] && [username isEqualToString:form.username] && form.neverForThisSite == NO) {
#ifdef DEBUG
                NSLog(@"Site already saved");
#endif
                return;
            }

            if ([[url host] isEqualToString:form.urlString] && form.neverForThisSite == YES) {
#ifdef DEBUG
                NSLog(@"Never for this site");
#endif
                return;
            }
        }

        weakSelf.formData = [[FormObject alloc] initWithUsername:username withUsernameID:usernameFieldID withUsernameName:usernameFieldName withPassword:password withPasswordID:passwordFieldID withPasswordName:passwordFieldName withURLString:[url host]];
        UIActionSheet *saveFormConfirmationActionSheet = [[UIActionSheet alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Would you like to save this password?", @"Would you like to save this password?")] delegate:weakSelf cancelButtonTitle:NSLocalizedString(@"Not Now", @"Not Now") destructiveButtonTitle:nil otherButtonTitles:NSLocalizedString(@"Yes", @"Yes"), NSLocalizedString(@"Never for this Website", @"Never for this Website"), nil];
        saveFormConfirmationActionSheet.tag = kSaveFormConfirmationActionSheetTag;
        [saveFormConfirmationActionSheet showFromToolbar:weakToolbar];
    };

    return context;
}
هل كانت مفيدة؟

المحلول

Accessing an instance variable inside a block will cause the block to capture a reference to the object that owns the instance variable - self.

You can avoid this by converting the instance variable to a property, creating a weak reference to self, and setting the property on the weak self.

Alternatively you can use -> to directly access the ivar, again on a weak self, but I prefer the former solution.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top