Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot assign to self out of a method in the init family?

Tags:

I am using self like "self = [super init];", the following code give me an error "cannot assign to self out of a method in the init family"

- (id)showDropDown:(UIButton *)b:(CGFloat *)height:(NSArray *)arr:(NSString *)direction {     btnSender = b;     animationDirection = direction;     self = [super init];     if (self) {         // Initialization code         CGRect btn = b.frame;         self.list = [NSArray arrayWithArray:arr];          if ([direction isEqualToString:@"up"]) {             self.frame = CGRectMake(btn.origin.x, btn.origin.y, btn.size.width, 0);             self.layer.shadowOffset = CGSizeMake(-5, -5);         }else if ([direction isEqualToString:@"down"]) {             self.frame = CGRectMake(btn.origin.x, btn.origin.y+btn.size.height, btn.size.width, 0);             self.layer.shadowOffset = CGSizeMake(-5, 5);         }          self.layer.masksToBounds = NO;         self.layer.cornerRadius = 8;         self.layer.shadowRadius = 5;         self.layer.shadowOpacity = 0.5;          table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, btn.size.width, 0)];         table.delegate = self;         table.dataSource = self;         table.layer.cornerRadius = 5;         table.backgroundColor = [UIColor colorWithRed:0.239 green:0.239 blue:0.239 alpha:1];         table.separatorStyle = UITableViewCellSeparatorStyleSingleLine;         table.separatorColor = [UIColor grayColor];          [UIView beginAnimations:nil context:nil];         [UIView setAnimationDuration:0.5];         if ([direction isEqualToString:@"up"]) {             self.frame = CGRectMake(btn.origin.x, btn.origin.y-*height, btn.size.width, *height);         } else if([direction isEqualToString:@"down"]) {             self.frame = CGRectMake(btn.origin.x, btn.origin.y+btn.size.height, btn.size.width, *height);         }         table.frame = CGRectMake(0, 0, btn.size.width, *height);         [UIView commitAnimations];          [b.superview addSubview:self];         [self addSubview:table];     }     return self; } 

Error:

 Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Requesting the window of a view (<NIDropDown: 0x684ac50; frame = (0 0; 0 0); transform = [0, 0, 0, 0, 0, 0]; alpha = 0; opaque = NO; layer = (null)>) with a nil layer. This view probably hasn't received initWithFrame: or initWithCoder:. 
like image 340
Adnan Khan Avatar asked Apr 15 '13 10:04

Adnan Khan


2 Answers

If your method is an initializer, it must start with init:

- (instancetype)initWithDropDown:(UIButton *)b:(CGFloat *)height:(NSArray *)arr:(NSString *)direction

Otherwise, you can change it to be a class method, and return a new instance:

+ (instancetype)showDropDown:(UIButton *)b:(CGFloat *)height:(NSArray *)arr:(NSString *)direction {     btnSender = b;     animationDirection = direction;     YourClassName obj = [[self alloc] init];     if (obj) {         // Initialization code         // …     }     return obj; } 
like image 114
Marcelo Fabri Avatar answered Oct 01 '22 09:10

Marcelo Fabri


You can't initialize self outside an init method because of the following convention:

If an object’s class does not implement an initializer, the Objective-C runtime invokes the initializer of the nearest ancestor instead.

and because the name of some methods is recognized by the memory management system of the runtime:

When the name of a method starts with alloc init, retain, or copy, it means it's being created for the caller, who has the responsibility to release the object when he is done with it. Otherwise the method returned is not owned by the caller, and he has to indicate he wants to keep it calling retain on the object.

Therefore, all initializers should be written as a variation of this:

- (id) init {     self = [super init];     if (self){         _someVariable = @"someValue";     }     return self; } 
  • Don't use if ((self = [super init])) because it's ugly.
  • Don't use self.someVariable because the object may not be initialized yet. Use direct variable access instead (_someVariable).
  • We write self = [super init] and not just [super init] because a different instance may be returned.
  • We write if (self) because there will be cases where it will return nil.

However, there are two more problems with your method:

  • You are combining object creation ([super init]) and an action (-showDropDown::::) in the same method. You should write two separate methods instead.
  • The name of your method is -showDropDown::::. Objective-C programmers expect self documenting method names like -showDropDown:height:array:direction: instead. I guess you come from a different language, but when in Rome, do as Romans do, or else you won't be playing along with the rest of the team.
like image 40
Jano Avatar answered Oct 01 '22 09:10

Jano