NSPlaceholderDictionary: попытка вставить нулевой объект из objects[1]

#ios #objective-c #uitextfield

#iOS #objective-c #uitextfield

Вопрос:

У меня есть два UITextField поля, одно для minPrice и одно для maxPrice. Когда я пытаюсь запустить функцию, запускаемую submitButton IBAction , используя эти два содержимого текстового поля в качестве параметров, она сообщает, что объекты пусты со следующей ошибкой:

'*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]'

Кажется, я не могу понять, почему текст, вводимый в эти поля, не сохраняется должным образом. Я что-то забываю, когда программно инициализирую их?

CriteriaViewController.h:

 #import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import "SearchViewController.h"

@interface CriteriaViewController : UIViewController{

IBOutlet UISegmentedControl *Segment;
}


//@property (nonatomic) IBOutlet UITextField *itemSearch;
@property (weak, nonatomic) IBOutlet UITextField *minPrice;
@property (weak, nonatomic) IBOutlet UITextField *maxPrice;
@property (nonatomic, copy) NSNumber *chosenCategory;
@property (nonatomic, copy) NSString *chosenCategoryName;
@property (weak, nonatomic) NSString *itemCondition;
@property (weak, nonatomic) NSString *itemLocation;
@property (weak, nonatomic) NSString *itemSearch;

@end
  

CriteriaViewController.m:

 #import "CriteriaViewController.h"

@interface CriteriaViewController ()
@property (weak, nonatomic) IBOutlet UISegmentedControl *itemConditionSegment;
@property (weak, nonatomic) IBOutlet UISegmentedControl *itemLocationSegment;


@end

@implementation CriteriaViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{

    [super viewDidLoad];

    [self addMinTextField];
    [self addMaxTextField];


    // Condition UISegment
    UISegmentedControl *conditionSegmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Only New", @"Any", nil]];
    conditionSegmentedControl.frame = CGRectMake(87, 190, 157, 30);
    conditionSegmentedControl.selectedSegmentIndex = 0;
    conditionSegmentedControl.tintColor = [UIColor blackColor];
    [conditionSegmentedControl addTarget:self action:@selector(ConditionSegmentControlAction:) forControlEvents: UIControlEventValueChanged];
    [self.view addSubview:conditionSegmentedControl];


    // Location UISegment
    UISegmentedControl *locationSegmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Fast Shipping", @"Large Selection", nil]];
    locationSegmentedControl.frame = CGRectMake(67, 275, 200, 30);
    locationSegmentedControl.selectedSegmentIndex = 0;
    locationSegmentedControl.tintColor = [UIColor blackColor];
    [locationSegmentedControl addTarget:self action:@selector(LocationSegmentControlAction:) forControlEvents: UIControlEventValueChanged];
    [self.view addSubview:locationSegmentedControl];




    // Submit button
    UIButton *submitButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // Create Round Rect Type button.
    submitButton.frame = CGRectMake(100, 100, 100, 100); // define position and width and height for the button.
    [submitButton setTitle:@"Submit" forState:UIControlStateNormal];

    [submitButton addTarget:self action:@selector(submitButton:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:submitButton];



}



-(void)addMinTextField{
    // This allocates a label
    UILabel *prefixLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    // This sets the font for the label
    [prefixLabel setFont:[UIFont boldSystemFontOfSize:14]];
    // This fits the frame to size of the text
    [prefixLabel sizeToFit];

    // This allocates the textfield and sets its frame
    UITextField *minPrice = [[UITextField  alloc] initWithFrame:
                             CGRectMake(70, 105, 75, 30)];

    // This sets the border style of the text field
    minPrice.borderStyle = UITextBorderStyleRoundedRect;
    minPrice.contentVerticalAlignment =
    UIControlContentVerticalAlignmentCenter;
    [minPrice setFont:[UIFont boldSystemFontOfSize:12]];

    //Placeholder text
    minPrice.placeholder = @"150";

    //Prefix label is set as left view and the text starts after that
    minPrice.leftView = prefixLabel;

    //It set when the left prefixLabel to be displayed
    minPrice.leftViewMode = UITextFieldViewModeAlways;

    // Adds the textField to the view.
    [self.view addSubview:minPrice];

    // sets the delegate to the current class
    minPrice.delegate = self;
}

-(void)addMaxTextField{
    // This allocates a label
    UILabel *prefixLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    // This sets the font for the label
    [prefixLabel setFont:[UIFont boldSystemFontOfSize:14]];
    // This fits the frame to size of the text
    [prefixLabel sizeToFit];

    UITextField *maxPrice = [[UITextField  alloc] initWithFrame:
                             CGRectMake(185, 105, 75, 30)];

    //for max price
    maxPrice.borderStyle = UITextBorderStyleRoundedRect;
    maxPrice.contentVerticalAlignment =
    UIControlContentVerticalAlignmentCenter;
    [maxPrice setFont:[UIFont boldSystemFontOfSize:12]];

    //Placeholder text is displayed when no text is typed
    maxPrice.placeholder = @"300";

    //Prefix label is set as left view and the text starts after that
    maxPrice.leftView = prefixLabel;

    //It set when the left prefixLabel to be displayed
    maxPrice.leftViewMode = UITextFieldViewModeAlways;

    // Adds the textField to the view.
    [self.view addSubview:maxPrice];

    // sets the delegate to the current class
    maxPrice.delegate = self;
}



- (void)ConditionSegmentControlAction:(UISegmentedControl *)segment
{
    if(segment.selectedSegmentIndex == 0)
    {
        // set condition to new
        self.itemCondition = @"new";
    }
    else if (segment.selectedSegmentIndex == 1)
    {
        // set condition to all
        self.itemCondition = @"all";
    }
}

- (void)LocationSegmentControlAction:(UISegmentedControl *)segment
{
    if(segment.selectedSegmentIndex == 0)
    {
        // set location to us
        self.itemLocation = @"US";
    }
    else if (segment.selectedSegmentIndex == 1)
    {
        // set clocation to worldwide
        self.itemLocation = @"Worldwide";
    }
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}






//add all the info to users respective new category object
- (IBAction)submitButton:(id)sender
{
    //if (self.minPriceField.text.length > 0 amp;amp; self.maxPrice.text.length > 0) {

        [PFCloud callFunctionInBackground:@"userCategorySave"
                           withParameters:@{@"categoryId": self.chosenCategory,
                                              @"minPrice": self.minPrice,
                                            @"maxPrice": self.maxPrice,
                                       @"itemCondition": self.itemCondition,
                                        @"itemLocation": self.itemLocation,
                                        @"categoryName": self.chosenCategoryName,
                                            }
                                         block:^(NSString *result, NSError *error) {

                                             if (!error) {
                                                 NSLog(@"Criteria successfully saved.");

                                                     [self performSegueWithIdentifier:@"SearchCategoryChooserToMatchCenterSegue" sender:self];

                                             }
                                         }];

    //}

}


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {



//    [PFCloud callFunctionInBackground:@"addToMatchCenter"
//                       withParameters:@{
//                                        @"searchTerm": self.itemSearch,
//                                        @"categoryId": self.chosenCategory,
//                                        @"minPrice": self.minPrice,
//                                        @"maxPrice": self.maxPrice,
//                                        @"itemCondition": self.itemCondition,
//                                        @"itemLocation": self.itemLocation,
//                                        }
//                                block:^(NSString *result, NSError *error) {
//                                    
//                                    if (!error) {
//                                        NSLog(@"'%@'", result);
//                                    }
//                                }];

    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.

}

@end
  

Комментарии:

1. У вас есть minPrice и maxPrice в качестве IBOutlets, и все же вы создаете их в коде. Почему? Есть ли у вас эти текстовые поля в представлении в раскадровке или xib?

2. Вы уверены, что self.minPrice и self.maxPrice не равны нулю? Используйте отладчик и просмотрите все значения непосредственно перед вызовом PFCloud callFunction... .

3. Вы используете локальные переменные minPrice и maxPrice в ваших методах addMinTextField и addMaxTextField, но вы ссылаетесь на свои свойства self.minPrice и self.maxPrice в вашем методе submitButton. Итак, если у вас на самом деле нет IBOutlets (или они не подключены), то self.minPrice и self.maxPrice будут равны нулю.

4. @Ghobs: подумайте о том, чтобы переписать заголовок этого вопроса, чтобы отразить нулевой указатель в NSDictionary.

Ответ №1:

Вы создаете, NSDictionary используя сокращение @{ key, value} с одним или несколькими nil значениями.

В качестве превентивного метода на этапе разработки могу я предложить проверить все ваши value(s) использования:

 NSParameterAssert(self.itemSearch);
NSParameterAssert(self.chosenCategory);
// etc.
  

Это выдаст assert и сообщит вам, что ваша конструкция NSDictionary обречена на неудачу.

Теперь о причинах (в вашем коде)… Как указано в комментариях, вы локально переопределяете область действия членов вашего класса локальными переменными:

 // This allocates the textfield and sets its frame
UITextField *minPrice = ...
  

Рассмотрите возможность использования этой конструкции (если, конечно, нет, создания объектов пользовательского интерфейса с помощью IB, что вы, похоже, делаете)

 self.minPrice = ({
    UITextField *minPrice = [[UITextField alloc] init...];
    // more initialization of minPrice...

    minPrice;
});
[self.view addSubview:self.minPrice];