Initializing a property through the private attribute - objective-c

I'm learning Objective-C with the Big Nerd Ranch Guide.
The author uses a store from which the controller can get some data to display :
#import "BNRItemStore.h"
#import "BNRItem.h"
#interface BNRItemStore ()
#property(nonatomic) NSMutableArray *privateItems;
#end
#implementation BNRItemStore
+(instancetype)sharedStore {
static BNRItemStore *sharedStore = nil;
if (!sharedStore){
sharedStore = [[BNRItemStore alloc] initPrivate];
}
return sharedStore;
}
-(instancetype)initPrivate {
self = [super init];
if (self) {
_privateItems = [[NSMutableArray alloc] init];
}
return self;
}
My question is about the _privateItems = [[NSMutableArray alloc] init]; line : why do we initialize _privateItems and not privateItems ?
Regards.

privateItems is the property name. Each property has a variable behind it, with a default name of _propertyName. in your case it's _privateItems.
Most times, you would use the property to set the value, like this: self.privateItems = [[NSMutableArray alloc] init]. However, you should not set properties directly in init methods, and that is why the author sets the value directly to the variable.

Related

Objective-c : cannot init a class object

I am getting an issue cannot init a class object even after adding lines to the constructor such as
self = [super init];
or
self = [[super init] alloc];
And I am not sure what to do.
This is the specific error:
file:///%3Cunknown%3E: test failure: -[LinkedListTest testAdd] failed: *** +[NList<0x8e14> init]: cannot init a class object.
.m
#interface NList()
#property (weak, nonatomic, readwrite) NSObject *head;
#property (nonatomic,readwrite) NSInteger *size;
#end
#implementation NList
#synthesize size = _size;
- (id) init:(NSInteger *)size {
//is this even necessary? I don't want object methods.. or do I ?
if (self){
_head = nil;
_size = size;
}
return self;
}
.h
#interface NList : NSObject
#property (nonatomic,readonly) NSInteger *size;
#property (weak, readonly, nonatomic) NSObject *head;
- (void)add:(NSObject *)node;
#end
test class
- (void)testAdd
{
NList *testList = [[NList init] alloc];
// Card *testCardOne = [[Card init] alloc];
// [testList add:(testCardOne)];
XCTAssertNotNil(testList.head);
}
I have tried adding the line
self = [[super init] alloc];
to the constructor to no avail.
No visible interfacce for nlist declares
or self = [super init]
complains cannot init a class object!
EDIT
I realized that it is not asking me for the size! the constructor requires a size parameter...how do I do this! Ahh [looks up docs]
A few things.
You need a default constructor
- (id)init {
self = [super init];
if (self) {
self.head = nil;
}
return self;
}
Now that you have a default constructor (that calls the superclasses constructor), you need a more specific one for your purposes.
- (id)initWithSize:(int)size {
self = [self init]; // sets head, calls super constructor.
if (self) {
self.size = size;
}
return self;
}
Edit: Note, the last one had to be in your .h file so it is visible.
And also, when instantiating this class, call
NList *list = [[NList alloc] initWithSize:mySize];
You're a little backwards.
How about:
NList *testList = [[NList alloc] init:SIZE];
where size is the SIZE init you want to use.
Alloc comes before init when you're instantiating Objective-C objects.

Why can't I populate my controller with items?

I'm using an ItemController to provide a list of items to use in a tableview. I can't seem to populate the controller though, and I'm not sure why.
Here's the code for the controller class:
.h
#import <Foundation/Foundation.h>
#class Item;
#interface ItemController : NSObject
#property (nonatomic, copy) NSMutableArray *items;
- (NSUInteger)countOfList;
- (Item*)objectInListAtIndex:(NSUInteger)theIndex;
- (void)addItem:(Item *)item;
#end
.m
#import "ItemController.h"
#import "Item.h"
#interface ItemController ()
#end
#implementation ItemController
- (NSUInteger)countOfList {
return [self.items count];
}
- (Item *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.items objectAtIndex:theIndex];
}
- (void)addItem:(Item *)item {
[self.items addObject:item];
}
#end
Item.m
#implementation Item
-(id)initWithName:(NSString *)name{
self = [super init];
if (self) {
_name = name;
return self;
}
return nil;
}
#end
I'm using the following code to populate the list:
ItemController* controller = [[ItemController alloc] init];
for (NSString* key in raw_data) {
NSLog(key); // This outputs the keys fine
[controller addItem:[[Item alloc] initWithName:key]];
}
NSLog([NSString stringWithFormat:#"%d",[controller countOfList]]); // Always 0
You need to initialize the array in the init methond.
- (id)init {
self = [super init];
if (self) {
self.items = [[NSMutableArray alloc] init];
}
return self;
}
You need to initialize your variable items. In your init method, call self.items = [NSMutableArray new]; and also change your array property from copy to retain.
I also believe your class ItemController should be of kind UIViewController and not NSObject.
#interface ItemController : UIViewController
You don't initialise the _items instance variable anywhere, so it's always nil. The result of any integer-returning method called on nil will be 0, so you see that the count is 0.

Dynamic link / Dynamic typing in objective-c

I have seen in the BNR 3rd edition an example to do a static singleton class.
To do that, they explain how to avoid a creation cycle calling the superclass alloc:
static MyClass *myclass = [[super alloc] init];
MyClass has its own init method.
NSObject -> MyClass
My doubt is: Which init class is sent? NSOject init, or MyClass init
Nested alloc init equals:
myclass = [super alloc] and then
myclass = [myclass init] ???????????
OR
myclass = [super alloc] and then myclass = [super init]
Do not use super, but do use self. Otherwise subclassing of your singleton will not work. The correct way is something like this:
+ (MyClass *)sharedInstance {
static MyClass *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
dispatch_once guarantees that your code (block in this case) is called only once. And self guarantees that subclassing will work properly.
for you question, the selector is always called on the subclass's implementation, which is your first guess
the way described in the book is a valid way to implement singleton, but i do not suggest it though. Robert Vojta's solution is good.
i cannot see how necessary to override allocWithZone, then you also need to make sure init does nothing (at least not leak)
Just for add some tests:
I have created 2 MyClass classes: NSObject -> Myclass -> My2ndClass
So:
#implementation Myclass
+(id) sharedClass {
static Myclass *miclase = nil;
miclase = [[self alloc] init];
NSLog(#"%#", [super description]);
return miclase;
}
-(id)init {
self = [super init];
NSLog(#"init de Myclass");
return self;
}
-(NSString *)description {
return #"i am Myclass";
}
#end
AND:
#implementation My2ndClass
+(id) sharedClass {
static My2ndClass *miclase = nil;
miclase = [[super alloc] init];
//miclase = [super init]; CRASH
NSLog(#"%#", [super description]);
return miclase;
}
-(id)init {
self = [super init];
NSLog(#"init de My2ndClass");
NSLog(#"%#", [super description]);
return self;
}
-(NSString *)description {
return #"i am My2ndClass";
}
#end
Then in AppDelegate:
Myclass *miclase = [Myclass sharedClass];
My2ndClass *mi2ndclase = [My2ndClass sharedClass];
This is the console output:
2012-09-03 17:18:55.742 Dynamic Typing[2891:207] init de Myclass
2012-09-03 17:18:55.744 Dynamic Typing[2891:207] Myclass
2012-09-03 17:18:55.746 Dynamic Typing[2891:207] init de Myclass
2012-09-03 17:18:55.747 Dynamic Typing[2891:207] init de My2ndClass
2012-09-03 17:18:55.748 Dynamic Typing[2891:207] i am Myclass
2012-09-03 17:18:55.751 Dynamic Typing[2891:207] My2ndClass
Like xlc0212 told, the correct messages when they are nested are:
miclase = [super alloc];
miclase = [miclase init];
Besides, if i do
miclase = [super alloc]
and then
miclase = [super init]
it CRASHES.
When is sent a class method (+) [super description], it logs class name (Myclass and My2ndClass). They are de class itself and don't have super object, do they?

NSMutabledictionary not working

I am new to Iphone programming. Please correct me where am I going wrong.
I have two viewcontrollers
viewcontroller1 viewcontroller2
In viewcontroller1,
-(IBAction) getQuestions:(id)sender
{
NSLog(#"In get questions..");
[[self viewcontroller2] initWithData:userInfo];
[self presentModalViewController:viewcontroller2 animated:YES];
[quesions autorelease];
}
In viewController2 I have the following code.
-(id)initWithData:(NSMutableDictionary*)data
{
self = [super init];
if(self)
{
userInfo = [[NSMutableDictionary alloc] initWithDictionary:data];
}
return self;
}
-(IBAction) getQuesionAfterPopUp:(id) sender
{
NSLog(#"In get question..After popup...%#",userInfo);
}
For some reason "userInfo" is null. Why is it null even after using init with data.
You didn't initialize correctly, therefore the instance variable userInfo is not accessible.
Initialization should look like this:
- (id)initWithData:(NSMutableDictionary *)data
{
self = [super init]; // 'self' and it's instance variables are accessible at this point ...
if (self)
{
userInfo = // etc...
}
return self;
}
You might want to read up on this link for more information on how to implement designated initializers. Especially focus on the topic Implementing an Initializer.
in the above code you don't retain your userInfo object. Try
userInfo = [[[NSMutableDictionary alloc] initWithDictionary:data]]retain];
Or if userInfo is a property like:
#property (nonatomic, retain)
Try:
self.userInfo = [[NSMutableDictionary alloc] initWithDictionary:data]];
Which will automatically retain the userInfo. Please note by not calling self you are assigning straight to the instance variable.
Finally, make sure that the data object is not empty

Also found '-(void) init'

I built a custom class named game:
.h
-(void) init;
here I have a Also found '-(void) init'
.m
-(void) init {
[super init];
score = 0;
lives = 3;
elements = [[NSMutableArray alloc] initWithCapacity:1000];
}
when I try to initialize a object with:
myGame = [[Game alloc] init];
I got "Multiple methods named '-init' found
So I don't know where the error is...
init should always return (id). Change your function to the following:
.h
-(id) init;
.m
-(id) init {
if((self = [super init]))
{
score = 0;
lives = 3;
elements = [[NSMutableArray alloc] initWithCapacity:1000];
}
return self;
}