What is Best practice in objective c for creating an object and setting its properties - iphone

Considering I have a UIViewController called ErrorViewController that I am instantiating using initWithNibName.
There is a enum on this ErrorViewController describing its "type".
This ErrorViewController has one delegate function that returns to its delegate which will respond according to the type set on the ErrorViewController.
Is it better to pass all the parameters within a new initWithNibName function, and set private properties on the ErrorViewController. Like this:
ErrorViewController *errorVc = [[ErrorViewController alloc]
initWithNibName:#"ErrorViewController" bundle:nil
andErrorType:kErrorTypeA andDelegate:self];
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
andErrorType:(ErrorType)errorType andDelegate:(id<ErrorDelegate>)delegate{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = delegate;
self.errorType = errorType;
}
return self;
}
Or is it better to instantiate the object and afterward set its public properties like this:
ErrorViewController *errorVc = [[ErrorViewController alloc]
initWithNibName:#"ErrorViewController" bundle:nil];
errorVc.delegate = self;
errorVc.type = kErrorTypeA.
And regarding the delegate method, is it best practice to check the type by passing a parameter, or by checking the property of the passed back Controller as follows:
- (void)ErrorPage:(ErrorViewController *)ErrorPage
// check ErrorPage.errorType
}
or this: ?
- (void)ErrorPage:(ErrorViewController *)ErrorPage
andErrorType:(ErrorType)errorType
// check errorType
}

I think it's a question of preference. If the object can't function correctly without error-type and/or delegate, it's probably best to provide your custom initialiser.
As to your second question, I would provide the error type as in your second example. Note that the method name should start with a lowercase character though (-errorPage: instead of -ErrorPage:).
Additionally, if you use it a lot, I would provide a convenience class method to create the object:
+(ErrorViewController*) standardErrorViewControllerWithErrorType: (ErrorType) errorType andDelegate: (id<ErrorDelegate>) delegate {
ErrorViewController *evc = [[ErrorViewController alloc] initWithNibName: #"ErrorViewController" bundle: nil andErrorType: errorType andDelegate: delegate];
return evc;
}
Edit
Also, in your init method, it is encouraged to use -(instancetype) init... instead of -(id) init....

Related

iOS Blocks - use of undeclared identifier self

I am new to blocks. I am inside a singleton and I do this
void (^ myBlock)() = ^(){ [self doStuff]; };
I receive this error use of undeclared identifier self.
doStuff is a method inside the singleton.
but if this block is declared inside another method, Xcode is OK.
Why is that? thanks.
you can define the block in your interface and initialize in any of your methods (including initializers ) in your #implementation file like below:
#interface YourClass {
void (^ myBlock)();
}
#implementation YourClass
- (void)yourMethod {
myBlock = ^(){ [self doStuff]; };
}
#end
You shouldn't call self directly in a block.
Rather you should make a safe block-pointer from self and access it inside your block.
__block id safeBlockSelf = self;
void (^ myBlock)() = ^(){ [safeBlockSelf doSomething]; };
See How do I avoid capturing self in blocks when implementing an API? for more details.
because every method gets passed self as a hidden param. self is a variable like any other and the block can 'see it/capture it' if in the method
if it is not in a method, self is not a variable set anywhere and the block cant 'see it'

Creating constructors in Objective-C

Why do we always do this when creating constructors in Objective C?
self = [super init];
if ( self ) {
//Initialization code here
}
you can create constructor and destructor in objective-c with
-(id) init
{
self = [super init];
if(self)
{
//do something
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
We reassign to self because [super init] is allowed to return a different object than the one it was called on. We if (self) because [super init] is allowed to return nil.
self is a class based on some superclass (e.g. UIViewController, NSObject - see your interface file to be sure which one). The superclass might need some form of initialization in order for the subclass to work as expected. So by first initializing the superclass we make sure default properties and the like are set. Without initializing the superclass first, we might experience some very unexpected behavior, especially in more complex objects like ViewControllers and the like.
Read this apple document on initialization
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html

Why put [super init] inside of an if statement since whether or not the return is nil we return it anyway?

On the CS193p course they says that in an init method there should be an if statement to check if the [super init] works:
if (self = [super init]) {
self.someProperty = parameter;
}
return self;
I don't understand why this is done, as if the [super init] returns nil, the method itself will also return nil, no matter the outcome of the if statement?
EDIT: The question is; why put self = [super init] inside an if statement. (Not: Why have self = [super init] at all)
That's make sense because in some cases [super init] can return nil, and in this case if you try to access some ivar you'll get crash.
Example:
-(id) init {
self = [super init];
some_ivar = [[NSObject alloc] init]; //compiler treats this as self->some_ivar
//if self == nil you'll get EXC_BAD_ACCESS
return self;
}
Apple explains this particular concept in detail in there Objective-c programming guide. (Scroll down to the section "Handling Initialization Failure")
However Apple example code often keeps the self assignment outside the if statement
self = [super init];
if (self) {
//init stuff...
}
return self;
And if you are using the LLVM 2.0 compiler, it will give you a warning and tell you to wrap your statement in parentheses, too. Like this:
if ((self = [super init])) {
//init stuff...
}
return self;
It should be if(self = [super init]){.... init is an instance method and thus can only be called on an object that has already been alloc'd.
You need to call [super init] so that the superclass can do any one-time initialization that it needs to do. The init method returns a pointer representing the object that was initialized.
Assigning the result of [super init] back to self is a standard Objective-C convention. It's done in case the superclass, as part of its initialization work, returns a different object than the one originally created. NSString's, for example, do this
So it's self = [super init]
I have never seen [[super alloc] init], i've always used [super init], and as far as my knowledge, that's the convention
It doesn't have to be in an if statement.
you can very well have it like
self = [super init];
if ( self ) {
self.someProperty = parameter;
}
return self;
the if statement is there to check that self was initialized properly and it is safe to do self.someProperty = parameter
self = [[super alloc] init]; creates a base class
and the runtime will choke when you try to invoke subclass-only methods
later on.
self = [super init]; is a common sight in Objective-C
when the base class needs to initialize variable values or instantiate composite members,
otherwise they are created with Nil or (0) values.

Using properties to access iVars in init?

This is an offshoot from a previous question, is this bad practice (using the property to set iVars)?
// Designated initializer 001
- (id)initWithName:(NSString *)newName andType:(NSString *)newType {
self = [super init];
if(self) {
[self setName:newName];
[self setType:newType];
}
return self;
}
or should I be using ...
// Designated initializer 002
- (id)initWithName:(NSString *)newName andType:(NSString *)newType {
self = [super init];
if(self) {
name = [newName retain];
type = [newType retain];
}
return self;
}
I have been using version 001, but have been led to believe that using properties to access iVars in either init or dealloc is bad practice.
EDIT: Added retain to version 002
Gary.
Yes, Apple discourages using accessors in init or dealloc, because they can have side effects beyond merely setting an instance variable. These are obviously undesirable in an uninitialized or destroyed object.
Exact quote from the docs: "The only places you shouldn’t use accessor methods to set an instance variable are in init methods and dealloc."

iPhone: Error when using -(id)initWithNibName

I am trying to use the following code, but I always get an error that I can find little information about:
- (id)initWithNibName:#"MyRidesListView" bundle:nil {
if ((self = [super initWithNibName:#"MyRidesListView" bundle:nil])) {
// Custom initialization
}
return self;
}
Error:
expected identifier before 'OBJC_STRING' token
This seems like a simple method to be calling. This is for a UINavigationController.
Ideas?
It looks like you are trying to implement a constructor method in a subclass of UIViewController or UINavigationController.
YOur syntax is a bit off. Without seeing what you are doing in a broader context I don't really know what is going on here, but this might help you out a bit. Its the closesest to your code while being syntactically correct.
- (id)initWithNibName:(NSString *)nibNameOrNull bundle:bundle {
if ((self = [super initWithNibName:nibNameOrNull bundle: bundle])) {
// Custom initialization
}
return self;
}
Then you can do this outside of your class:
[[MyRidesListView alloc] initWithNibNamed:#"MyRidesListView" bundle:nil];