I am new here, and already searched related articles like "Is “self” necessary?" and "Setting an Objective-C class property without using a self reference" However i still can't get a clear answer which can explain my case.
I have a simple class and my UI has two textfield and one button, here is the code:
#interface testViewController : UIViewController {
NSString *teststring_A;
NSString *teststring_B;
IBOutlet UITextField *textfield_1;
IBOutlet UITextField *textfield_2;
}
#property (nonatomic, retain) NSString *teststring_A;
#property (nonatomic, retain) NSString *teststring_B;
- (IBAction)action1:(id)sender;
- (IBAction)action2:(id)sender;
#end
#implementation testViewController
#synthesize teststring_A;
#synthesize teststring_B;
- (void)dealloc {
[super dealloc];
}
- (IBAction)action1:sender
{
teststring_A = textfield_1.text ;
NSLog(#"teststring_A in action 1 is : %#\n", teststring_A);
teststring_B = textfield_2.text ;
NSLog(#"teststring_B in action 1 is : %#\n", teststring_B);
}
- (IBAction)action2:(id)sender
{
NSLog(#"teststring_A in action 2 is : %#\n", teststring_A);
NSLog(#"teststring_B in action 2 is : %#\n", teststring_B);
}
the output is :
2010-11-19 15:32:14.827 test[419:207] teststring_A in action 1 is : 123
2010-11-19 15:32:14.829 test[419:207] teststring_B in action 1 is : 456
2010-11-19 15:32:14.927 test[419:207] teststring_A in action 2 is : 123
2010-11-19 15:32:14.929 test[419:207] teststring_B in action 2 is : {(
>
)}
And when click button, it triggers action1 first then action2. My problem is... in action2, the value of teststring_B becomes incorrect, sometimes the application even crashes. What confuses me is (1) why is the value of teststring_A correct??? (2) teststring_B is assigned by textfield_2.text which is not created with 'alloc', so suppose the pointer should exist all the time. then why teststring_B's value becomes incorrect in action2 ??? (3) in dealloc, I should release teststring_A and teststring_B, right? (i think so )
All I know is if I add 'self.', like 'self.teststring_B = textfield_2.text;' then there won't be problem. the value will be correct. So I would like to know the technical reason.
You are confusing variables and properties. Properties are backed by variables, but in reality they are methods.
Here, you define a property named teststring_B which retains anything that gets assigned to it (and releases the old value). The equivalent methods would look like this (simplified):
- (NSString *)teststring_B {
// Return the content of the variable teststring_B.
return teststring_B;
}
- (void)setTeststring_B:(NSString *)val {
// Retain new value.
[val retain];
// Release old value in variable teststring_B
[teststring_B release];
// Assign the new, retained value to variable teststring_B
teststring_B = val;
}
You can now use the property in two ways: either with [self setTeststring_B:foo]; or with self.teststring_B = foo;. The important point is that the later is just a convenient way of writing, the compiler will translate it into the first form, that is the compiler will turn the self.foo = bar; lines into [self setFoo:bar];.
Now that we have this explained, on to your crash: you've got a string value which is most likely autoreleased. Now you just plain assign it to the variable teststring_B, not the property. And you forgot to retain the value. The property would have retained that value for you.
Now the assigned value was autoreleased (it didn't know you've got a variable still pointing to it) and later a new object came to life at the exact same memory location (if you're lucky). In any case, the teststring_B variable is now not pointing to the text as you thought it would, but to some random object (or to garbage).
There are two ways to fix this:
// First retain, then release; it might be the same object
// and if you would release it first its retain count might
// drop to 0 and get cleaned up before you can retain it again.
NSString *tmp = [textfield_2.text retain];
[teststring_B release];
teststring_B = tmp;
// Better !
self.teststring_B = textfield_2.text;
Accessing the variable directly without using the self will not retain it. So when you accessing it later the variable got auto-released and makes your application crash.
so you can write
1) [self setTeststring_B:textfield_2.text]; or
2) the dot syntax self.teststring_B = textfield_2.text; or
3) teststring_b = [textfield_2.text retain]
What you are doing now is simple assignment. That can cause a crash if the object teststring_A or teststring_B point to is deallocated; this is also called a dangling reference.
The reason only simple assignment is happening is because you aren't accessing the setters through the #property semantics; you could get a retain on those NSString objects by doing self.teststring_A = textfield_1.text instead.
However, you should be using copy with NSString properties. See: NSString property: copy or retain?
In other words, you want this:
#property (nonatomic, copy) NSString *teststring_A;
#property (nonatomic, copy) NSString *teststring_B;
and this:
self.teststring_A = textfield_1.text ;
self.teststring_B = textfield_1.text ;
Related
I'm trying to write an app that has two scenes in it. The first page is a UITableView that will contain a list of note entries. The second scene has 2 text fields (note summary and note description). I'm entering details on the second scene and then clicking a "Save" button which saves the data:
NoteListViewController.m
- (IBAction)saveAndGoBack:(id)sender {
NSLog(#"NoteDetailViewController.m: %#", NSStringFromSelector(_cmd));
NSString * desc = [[NSString alloc]init];
NSString * detail = [[NSString alloc]init];
desc = _noteTitle.text;
detail = _noteDesc.text;
[[NoteStore sharedStore]createNoteWithDesc:desc AndDetail:detail];
[self.navigationController popViewControllerAnimated:YES];
}
NoteStore is a static sharedStore that I am saving the data into.
NoteStore.m
-(Notes *)createNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail {
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
Notes * newNote = [[Notes alloc]initNoteWithDesc:desc AndDetail:detail];
[self.privateItems addObject:newNote];
return newNote;
}
So, the note is added to an NSMutableArray called "privateItems". I confirmed that the Note object gets added properly.
*****The problem happens when I try to retrieve the Note object (desc and detail) from the privateItems array later on using an accessor method which has a public property in the NoteStore.h file called allItems (it's an NSArray readonly, nonatomic and a copy):
NoteStore.m
-(NSArray *)allItems{
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
return [self.privateItems copy];
}
Everytime I try to retrieve it, the first property (desc) comes up as nil while the second property (detail) has the data I saved in the second text field of the second scene. Why is the first field constantly coming up as nil???
Just for clarity, a Note object is declared as follows
#interface Notes : NSObject
// What are the properties of a note?
#property (nonatomic, weak) NSString * noteDesc;
#property (nonatomic, weak) NSString * noteDetail;
#property (nonatomic, weak) NSString * test;
// Designated Initializer
-(instancetype)initNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail;
#end
Any help would be greatly appreciated.
When you call the designated initialiser you pass in two NSString objects. At this point they are owned by the method where they are created.
When they are assigned to the properties they only have a weak reference and therefor the retain count is not bumped up. Weak references are good for things like delegate objects. In this case you want your objects to stick around, so by declaring them as strong you're saying I want these properties to stick around in memory and take ownership of them.
I create a NSMutableArray that I need as long as my app lives, lets call it suseranArray, just after the #implementation of my main class. This Array will hold several objects of a class called Vassal. A Vassal is simply:
1) A NSMutableString
2) Another NSMutableString
3) A NSMutableArray
4) Another NSMutable Array
Each Vassal created is also needed for the life of the app, and they never change.
These objects are made as (retain) properties in an .h file, synthesized in the .m file, and each given an alloc+init whenever the object Vassal is created during the init function. Each vassal has data filled in and stored in the suzerain Array. the 3rd item always has several elements, and after a bug appeared, I put a line to check if it is ever empty, but it never is, and life is good.
Now, later on when a certain Vassal object is needed, we try to access its 3rd property to fetch the data in there, and sometimes that array empty... I checked to see if it disappeared somehow, but it is always there on the debug, carrying a nice address like 0x2319f8a0 which makes sense since the NSMutableString just above it is at address 0x2319fb40 - (I was expecting 00000000 after a lot of headache). What is happening? I my head, I am creating an RETAINed objects, which retains data put in by default, and that object is put inside another, but somehow the data inside the array vanishes. What possible scenario could lead to this? Thank you for your time :)
Note: the last array currently just holds one item at this stage of development, and curiously enough, that one item is never missing, despite the two arrays being 'brothers'
Vassal.h
#interface Vassal : NSObject
#property (retain) NSMutableString *wordBody;
#property (retain) NSMutableString *wordCode;
#property (retain) NSMutableArray *wordRelations;
#property (retain) NSMutableArray *wordLinks;
#end
Vassal.m
#implementation Vassal:NSObject
#synthesize wordBody;
#synthesize wordCode;
#synthesize wordRelations;
#synthesize wordLinks;
-(NSObject*) init
{
if(self=[super init])
{
wordBody=[[NSMutableString alloc] init];
wordCode=[[NSMutableString alloc] init];
wordRelations=[[NSMutableArray alloc] init];
wordLinks=[[NSMutableArray alloc] init];
}
return self;
}
//Somewhere in Suseran:
-(void)fillStuff
{
...
Vassal *vassal=[Vassal new];
for (int i=0;i<[originalDataString length];i++)
{
...
[vassal.wordRelations addObject:anItem];
...
}
int errorTest=[vassal.wordRelations count];
if (errorTest==0)
{
//breakpoint here. Program NEVER comes here
}
[bigArrayOfVassals addObject:vassal];
}
//these arrays are never touched again but here:
-(void) getVassalstuff:(NSMutableString*)codeOfDesiredVassal
{
Vassal *aVassal;
for (int i=0;i<[bigArrayOfVassals count];i++)
{
aVassal=bigArrayOfVassals[i];
if ([codeOfDesiredVassal isEqualToString:aVassal.wordCode)
{
int errorTest=[aVassal.wordRelations count];
if (errorTest==0)
{
//yay! this breakpoint sometimes is hit, sometimes not,
//depending on code's mood. Why is this happening to me? :,(
}
}
}
}
I see that that you have properties that are mutable (which is itself a bad idea except for specific cases) and that you are retaining them.
Mutability means that if you have set the array as a property based on some other array, and if that original array is changed, the array in your property is also changed. It may be, and I don't know because you haven't shown any code, that you are emptying the original array, and thus changing the array you have as a property
Solutions:
My preferred solution is to use the immutable versions of these classes for your properties; NSString, NSArray and instead of retain use copy
A second solution is to leave the properties as mutable, but write a custom setter for each of them that stores a mutableCopy of the object that you pass in.
In both of these cases, your property will be a copy of the object used to set the property, so that if the object is changed outside of your class it will not affect your class's properties.
edited to add, after a comment
If you declare your property as
#property (copy) NSArray wordRelations;
Then simply writing
vassal wordArray = tempArray;
will do the same thing and is cleaner and more readable..
There is a need to run some code when one object get dealloc. E.g, I set up one observer which updates the label A's text when object B's name changed. When label A's retain count reach 0, I want to remove the observer from B.
Possible solutions:
1 Subclass and call the clean code in dealloc.
2 Create a wrapper class which able to run arbitrary code in dealloc and associate this object to label A. When A get dealloc, the associated object get dealloc too (suppose only A holds the strong ref to it), then the code get called.
I don't like the 1st one since it is so intrusive that makes it barely useless, need to subclass just for some easy stuff. So I am using No.2.
Do you have any comments? How do you do it?
I put my solution here in case someone needed.
#interface ExecuteWrapper : NSObject
#property (nonatomic, copy) void(^block)();
-(void)dealloc;
#end
#implementation ExecuteWrapper
-(void)dealloc{
if(self.block){
self.block();
}
}
#end
#implementation NSObject (SLUtil)
+(void)executeWhenDealloc:(NSObject *)object block:(void(^)())block{
static char key;
NSMutableArray *executeWrapperArray = [object associatedValueForKey:&key];
if ( executeWrapperArray == nil){
executeWrapperArray = [NSMutableArray array];
[object associateValue:executeWrapperArray withKey:&key];
}
ExecuteWrapper *executeWrapper = [[ExecuteWrapper alloc] init];
executeWrapper.block = block;
[executeWrapperArray addObject:executeWrapper];
}
#end
In client code
[NSObject executeWhenDealloc:labelA block:^{
// clean up code
}];
Note: Keep in mind that don't hold a strong ref to label A in the label
To start let me tell you I am a total Objective-C beginner. This is my problem:
I have a NSMutableArray that stores objects, (Player) that has the name of the player and his/her score.
I am able to add objects to the array using addObject, but I am having trouble traversing this array. This
is how I do it:
// Get the reference to the array
NSMutableArray *myarray = [delegate getArray];
// Create a numerator
NSEnumerator *e = [myarray objectEnumerator];
id object;
while (object = [e nextObject])
{
[object printPlayer];
}
The method printPlayer belongs to the Player class and it just prints the name and the score.
The problem is when I have three players in the array and I am trying to print the content, it reaches this error inside the printPlayer method:
Thread 1: EXC_BAD_ACCESS(code=1, address=0x0000008)
Strangely if I use NSLog(#"%#", object); instead of [object printPlayer]; it prints a reference to the object and does not reach any error.
Anyone could point me what could be the problem when I try to use [object printPlayer]
Cheers
Update 1:
This is my printPlayer method:
-(void) printPlayer
{
NSLog(#"\n\nName: %#\nScore: %d", playerName, playerScore);
}
Update 2:
Player.h:
#interface PROGPlayer : NSObject
#property (nonatomic, assign) NSString *playerName;
#property (nonatomic, assign) int playerScore;
-(id) init: (NSString *) n;
-(void) printPlayer;
#end
Player.m:
#import "PROGPlayer.h"
#implementation PROGPlayer
#synthesize playerName;
#synthesize playerScore;
/**
* Player's class constructor
* #param n Player's name
* #param s Player's score
*/
-init: (NSString *) n
{
if (!(self = [super init])) return nil;
else
{
playerName = n;
playerScore = 0;
}
return self;
}
-(void) printPlayer
{
NSLog(#"\n\nName: %#\nScore: %d", playerName, playerScore);
}
#end
It seems like your problem is in the way you're defining your properties.
You're using assign rather than strong, or copy.
In a nutshell, it's because strong implies that you want your object to be retained.
Using copy implies that you want to create a new copy of an object or a value and set that as value of your property... As Mario and Jarsen explain, using copy is better practice when working with arrays to prevent the array being mutated (i.e. values changed) while it is being enumerated / traversed. Using copy also retains the new object.
If you're using ARC and your objects are not retained, then they will be released automatically by the compiler.
Using assign means that you assume the new object has been retained elsewhere and that you don't want to retain it again.
I suppose what was happening is that you were assigning your variable to your property, but the variable was being released (and hence resulting in nil) and causing the crash.
Here are a few links:
New to Objective C: Need help understanding strong reference vs assign
Objective-C ARC: strong vs retain and weak vs assign
Clarification on assign, retain, copy, strong?
Your playerName property should best be copied instead of assigned
#property (nonatomic, copy) NSString *playerName;
When trying to access the assigned value, the object most likely is gone causing the bad access.
Also remember to release playerName in dealloc when you set the property to copy.
Cheers
You just want to enumerate the array?
for (CustomClass *object in myArray){
[object printPlayer];
}
Either what Mike Z said or the "crude":
for (int i = 0; i < myArray.count; i++) {
CustomClass* object = [myArray objectAtIndex:i];
[object printPlayer];
}
While there are more elegant schemes, you can clearly understand what this one is doing, and how an NS(Mutable)Array is just a simple analog to a standard C array.
I'm beginner in Objective C and Cocos2D
I read Features of use #property and #synthesize (cocos2d)
The comments were references to bugs in the memory. What are these bugs?
In my code I use:
//interface
{
CC_bla_bla *a;
}
#property(nonatomic, retain) CC_bla_bla *a;
//implementation
#synthesize a;
self.a=[CC_bla_bla load_value:123123]
//dealloc
[self.a release]
self.a = nil;
Within a class, I always use self.a for all manipulations. Is that bad?
And in what sense to use the "instance variable" a?
Properties are most commonly used for getting things to and from other view controllers. You can use properties just in a certain view controller but you have to be cautious.
Since you do:
#property(nonatomic, retain) CC_bla_bla *a;
That has a retain count of 1 which like you did, you must release it in the dealloc. But say you do a = [[CC alloc], etc.... Then it will have a retain count of two.
Hopefully you understand this. You will realize on your own when it is time to use properties.
Properties are just there to associate some "metadata" to your variables which will be used when you access to this one via the object. The #synthesize directive will generate the getter and the setter of the variable using the properties config.
For example:
self.a = [CC_bla_bla load_value:123123]; // The object is retained because of the property
// This is equivalent to the previous line
[self setA:[CC_bla_bla load_value:123123]];
// ------
// By the same way
self.a;
// is equivalent to
[self a];
// ------
// method generated by your property
- (void)setA:(CC_bla_bla *)newA
{
[newA retain];
[a release];
a = newA;
}
But if you use directly the variable without passing by the object you don't use the properties value. For example:
a = [CC_bla_bla load_value:123123]; // The object is not retained so you may have unexcepted behaviors
// A good solution
a = [[CC_bla_bla load_value:123123] retain];
I hope it'll help you to clarify some points. For further reading you can watch this tutorial.
I think you are doing right with your code.
call your property always with self. is good.
But be ware, your code is fine only if the [CC_bla_bla loadvalue:] is not retain the created objects. If your loadvalue function have retained the created object and a property would retain it again, then it should be released twice.