Freshly init array is empty - why? - objective-c

Newbie here (to C, objective-C, and iOS)...
For this code in my AppDelegate.m, why is the freshly initialized array empty? Looks to me like I give it some values. Clue1 etc are are series of UILabels and there are no issues noted by Xcode. I've been through SO quite a bit and this seems to be the way to do it. Xcode 4.2. Thanks!
- (id)init {
self = [super init];
if(self) {
// Make arrays containing the objects: this is the objective-C way!
self.clueLabs = [[NSMutableArray alloc] initWithObjects: Clue1, Clue2, Clue3, nil];
}
NSLog(#"%#", [clueLabs description]);
if (!clueLabs || ![clueLabs count]) { // these 2 do m/l the same thing
NSLog(#"clueLabs is empty!");
}
return self;
}

Check if Clue1, Clue2 Clue3 are not nils.

Related

My NSString is determined to equal null, infuriating

This has been bugging me all night, It doesn't make any sense. This function returns whatever it's supposed to. EG, the issueName.
-(id)initWithIssue:(NSString *)string {
self = [super initWithNibName:nil bundle:nil];
if (self) {
NSString *thing = string;
issueName = [[NSString alloc]initWithString:thing];
NSLog(#"The issue name = %#", issueName);
}
return self;
}
However if I try to access 'issueName' in the viewDidLoad: nothing, it's equal to null no matter what I do. I've tried cleaning, setting a custom setter, switching between a property or a Ivar... ect. What's so infuriating is that this string just disappears at this point in the programe.
What the hell is going on, this is infuriating.
Edit
This the the entire code that is relevant. And how I started off.
Dot h file:
#interface BFPaidAreaViewController : UITabBarController <BFNewsTableViewControllerDelegate> {
NSString *issueName;
}
-(id)initWithIssue:(NSString *)string;
Dot m file:
-(id)initWithIssue:(NSString *)string {
self = [super init];
if (self) {
// PLPiper I had it that way before, because I was fiddling out of frustration
issueName = [[NSString alloc] initWithString:string];
NSLog(#"This is Called, the issue name is equal to = %#", issueName);
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSLog(#"The issue = %#", issueName);
}
I'm calling the view controller like so:
BFPaidAreaViewController *pavc = [[BFPaidAreaViewController alloc]initWithIssue:#"test"];
This will log:
This is Called, the issue name is equal to = test
The issue = (null)
New Edit
Found the problem. It's a UITableViewController. Strange, when I change it's class to a UIViewController it works. Is this a bug or just normal behaviour? But more pressing, how to I get round this limitation?
(Just to explain what I've done UI wise, the UITabBarController is in a modal View. This works fine with a UIViewController.)
God Awful Fix
-(id)initWithIssue:(NSString *)string {
self = [super initWithNibName:nil bundle:nil];
if (self) {
NSString *thing = string;
issueName = [[NSString alloc]initWithString:thing];
NSLog(#"The issue name = %#", issueName);
}
[self viewDidLoad];
return self;
}
Makes me feel dirty. But it will have to do for now, I can continue. If anyone can think of a solution please tell. Sorry about my feistiness, it was incredibly frustrating listening to people say, 'what the hell is this?? what is issueName?? an ivar??' when it was really implicit in the question.
Okay, first of all, replace:
self = [super initWithNibName:nil bundle:nil]; // Unneeded nil arguments
with:
self = [super init]; // Equivalent method, less processing involved.
Secondly, replace:
NSString *thing = string;
issueName = [[NSString alloc]initWithString:thing];
with just:
_issueName = [[NSString alloc] initWithString:string];
If issueName is a property (and you haven't #sythesized it to anything else) its representation should be _issueName.
The above fixes are more or less just make the code more succinct. The issue is probably with the code in viewDidLoad: (See below).
Now you can initialise your Issue object, and use the following code to display the issue name:
// Init:
Issue *myIssue = [[Issue alloc] initWithIssue:#"Example Issue"];
// Log:
NSLog(#"%#", myIssue.issueName);
And the log should show:
Example Issue
can you try this:
make the issueName a property, like
#property (strong, nonatomic) NSString *issueName;
then use it like this,
-(id)initWithIssue:(NSString *)string {
self = [super initWithNibName:nil bundle:nil];
if (self) {
NSLog(#"The string = %#", string);
self.issueName = string;
NSLog(#"The issue name = %#", issueName);
}
return self;
}
if you are using the automated synthetized property (i.e not declaring the #synthentize manually for the issueName), then your iVar will be called _issueName instead of issueName
what do you get from the above code ?
I find this somewhat curious. You call [super initWithNibName:nil bundle:nil]. This leads me to believe that this might be a subclass of NSViewController. If you init an NSViewController like this, barring some other, pretty non-standard stuff, -viewDidLoad probably won't get called because there's no NIB to be loaded (because you passed nil to super). But clearly you're setting a breakpoint in -viewDidLoad so it's getting called (on something). This makes me think that you have this class specified in a XIB somewhere as a File's Owner or as a NIB-loaded custom object. If that's the case, it leads me to believe that the instance you're init-ing and the instance on which -viewDidLoad is being called aren't the same instance. You can confirm this for yourself by putting NSLog(#"self: %p", self); in each method and seeing whether they are the same or different.
If the instance that is getting a call to -viewDidLoad is NIB-loaded, then your init method won't be called. Instead it will use -initWithCoder
If you can elaborate on the situation here (i.e. how this is getting instantiated, are there any XIBs involved, etc), I will edit my answer to provide more help, but I don't think there's enough information here to be truly helpful.
I feel your frustration. Assuming standard behavior, any of the suggestions here should have worked. This only reinforces my suspicion that these are not the same instance (between -initWithIssue and -viewDidLoad.

NSMutable Array returning NULL when NSLog

So I have an NSMutable array that I populate in viewDidLoad and then I NSLog it and it returns NULL in the console... I have looked around on stack-overflow and the apple reference and cannot find a solution to my problem.
Heres the code I used:
//Populate array
-(void)populateArray{
[TextArray addObject:#"Text1"];
[TextArray addObject:#"Text2"];
[TextArray addObject:#"Text3"];
[TextArray addObject:#"Text4"];
[TextArray addObject:#"Text5"];
NSLog(#"%#",TextArray);
NSLog(#"%#",[TextArray objectAtIndex:1]);
}
-(void)viewDidLoad
{
TextArray = [[NSMutableArray alloc] init];
[self populateArray];
}
And in the .h file
NSMutableArray *TextArray;
Thanks in advanced!
~Matt
the code you posted does not contain enough information for us to provide a specific answer. chances are, the methods are not being called in the order you expect them (e.g. populateArray is called from another point before viewDidLoad).
some assertions should help diagnose this:
//Populate array
-(void)populateArray
{
assert(nil != self.textArray && "you need to create an array");
[self.textArray addObject:#"Text1"];
[self.textArray addObject:#"Text2"];
[self.textArray addObject:#"Text3"];
[self.textArray addObject:#"Text4"];
[self.textArray addObject:#"Text5"];
assert(5 == [self.textArray count] && "populateArray has been called twice or serious threading error");
NSLog(#"%#",textArray);
NSLog(#"%#",[textArray objectAtIndex:1]);
}
-(void)viewDidLoad
{
assert(nil == self.textArray && "you need to clear your array (e.g. in viewDidUnload) or check if it has already been populated");
self.textArray = [NSMutableArray array];
[self populateArray];
assert(5 == [self.textArray count] && "populateArray has been called twice or serious threading error");
}
lastly, if this array never changes, it's going to be much simpler to create this in your initializer rather than lazily. such an array will not require much memory, in case that was your concern.
Update the construct takes this form:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
self = [super initWithNibName:nibName bundle:nibBundle];
if (nil != self){
textArray = [[NSArray alloc] initWithObjects:#"Text1", #"Text2", #"Text3", #"Text4", #"Text5", nil];
// ...
}
return self;
}
lastly, viewDidLoad may be certainly be called multiple times on the same instance. the system unloads (then lazily reloads) views in low memory conditions.

Do I need to allocate NSStrings passed in as parameters to a custom initialization method?

Please consider the following two initialization methods.
The first method simply passes the value of the parameters to their respective NSString properties, but the second allocates the properties and then initializes them using the initWithString: method. Is the allocation in the latter example necessary?
Thanks in advance.
-(id)initWithTitle:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = theTitle;
muscleGroup = theMuscleGroup;
equipment = theEquipment;
}
return self;
}
-(id)initWithTitle2:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = [[NSString alloc] initWithString:theTitle];
muscleGroup = [[NSString alloc] initWithString:theMuscleGroup];
equipment = [[NSString alloc] initWithString:theEquipment];
}
return self;
}
The first example is not safe because you are not taking ownership of the strings, so your program will get all crashy if they are later released elsewhere. The second example fixes that problem and will work perfectly well, but is more concisely written thusly:
-(id)initWithTitle2:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = [theTitle copy];
muscleGroup = [theMuscleGroup copy];
equipment = [theEquipment copy];
}
return self;
}
NSString gives you a copy constructor (-initWithString:), which enables you to do what you are doing in #2, but not all classes do. copy requires the class to implement the NSCopying protocol, but is more conformant with the way a Cocoa API developer would expect to be able to copy objects.
Parameter objects don't get copied when you pass them in. So your first example may not always work, it depends how you've initialized your strings.
The following is safer (although remember to release the objects in your dealloc method):
-(id)initWithTitle:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = [theTitle retain];
muscleGroup = [theMuscleGroup retain];
equipment = [theEquipment retain];
}
return self;
}
Example 1 will assign the pointers. It makes no attempt to retain the objects and is vulnerable to something outside changing the content of the objects.
It could work depending on how the arguments are constructed in the first place;
Example 2 will copy the string objects and retain them. As long as you release in the dealloc then its the preferable method.
FWIW
title = [theTitle copy];
or
title = [[NSString stringWithString:theTitle] retain];
are equally good in Ex 2

Objective C Reassignment/Memory Management Crash

As a relative Objective-C beginner, I'm obviously still not grasping certain memory management rules. I can't figure out how to make this not crash:
#interface MyClass { NSArray *playerArray4th; }
- (void) viewDidLoad { playerArray4th = [self getAudioPlayersForSoundFile:#"rimshot" ofType:#"aif"]; }
- (NSArray*) getAudioPlayersForSoundFile:(NSString*)soundFileName ofType:(NSString*)soundFileType {
//code instantiating objects....
NSArray *toRet = [[NSArray alloc] initWithObjects:toRetTickPlayer,toRetTickPlayerCopy,toRetTickPlayerCopy2,toRetTickPlayerCopy3, nil];
return toRet;
}
Then later, in a different function:
NSArray *currentArray = playerArray4th;
[currentArray release];
currentArray = nil;
currentArray = [self getAudioPlayersForSoundFile:fileName ofType:ofType];
And it crashes when trying to access the array again:
- (void) playSound:(NSString*)soundType {
AVAudioPlayer *currentPlayer;
if ([soundType isEqualToString:#"4th"]) {
if (playerArray4thCounter >= [playerArray4th count]) playerArray4thCounter = 0;
NSLog(#"Playing from array: %#",playerArray4th);
currentPlayer = [playerArray4th objectAtIndex:playerArray4thCounter];
playerArray4thCounter++;
}
}
Try to learn about properties and about using getters and setters. Don't take shortcuts unless you know exactly what's going on.
So define the playerArray4th property in your header file:
#property (nonatomic,retain) NSArray *playerArray4th;
And then in your .m file create getter/setter:
#synthesize playerArray4th;
Then, always use self.playerArray4th for assigning and getting the variable. The prior objects will be released when needed.
So this will not leak:
self.playerArray4th = [NSArray arrayWithObjects:#"text",#"text",nil];
self.playerArray4th = [NSArray arrayWithObjects:#"new array",#"text",nil];
because the second assignment releases the first array.
Furthermore, read about using autorelease. In short, if you alloc, copy or new, you should either release or autorelease. There's a lot to read about this here on SO which I will not repeat here now.
Don't forget to put self.playerArray4th = nil; in your dealloc method.

What would be a proper way to initialize a instance variable in Objective C without leaking memory?

I have a class like this:
#interface MyCollection : NSObject {
NSMutableDictionary *data;
}
and in the implementation of it, I have a method to init it like this:
- (id) init {
if(self = [super init])
{
self.data = [[NSMutableDictionary alloc] init];
}
return self;
}
Now when I create a object of this class in my code like this:
MyCollection *c = [[MyCollection alloc] init];
... at which point the Leaks utility shows that I have a memory leak in the init function on the very line where I try to set up the instance variable. I am totally new to Objective C & Iphone and I can't just get what is going wrong here. I have read through the Memory Management Guide and all, but I think I'm missing something pretty serious here.
Any help would be greatly appreciated. Thanks for your time already.
you are using self.data =. So there is most likely a property. And it most likely is a property which either copies or retains your object if you use it.
By calling
self.data = [[NSMutableDictionary alloc] init];
The retain count of the NSMutableDictionary increases because of the alloc, and if the property of data has a retain or copy statement you get another increase in retain count.
you could write data = [[NSMutableDictionary alloc] init]; or self.data = [NSMutableDictionary dictionary]. This would increase the retain count only one time.
And don't forget to release the object in dealloc.
You have to release the object in your dealloc method. That's why it's showing up as a leak.
to add to what fluchtpunkt mentioned you could try this instead:
- (id) init {
if(self = [super init])
{
self.data = [NSMutableDictionary dictionaryWithCapacity:0];
}
return self;
}
and in the dealloc
-(void)dealloc
{
self.data = nil;
}
I see weird situations with the Leaks utility as sometimes it reports old leaks, sometimes it doesn't report new ones, and so on. Also, from what I could collect with all your answers and opinion elsewhere on the web, people are divided on whether one should set a pointer to nil or not.
As of now, I have solved the situation with the following approach.
- (id) init {
if(self = [super init])
{
data = [[[NSMutableDictionary alloc] initWithCapacity:0];
}
return self;
}
-(void)dealloc
{
[data release];
}
Thanks everyone for contributing.
Are you creating the instance of "MyCollection" in the interface section?
If it has method scope try to release it in the same method after you are done with it.