Using property in objective-C
Disclaimer (read only !) : The original translations of all tutorials provided by this blog are from the Internet and are only for learning and communication purposes. Do not conduct commercial communications. At the same time, do not remove this statement when reprinting. In the event of any dispute, it has nothing to do with the owner of this blog and the person who published the translation. Thank you for your cooperation!
Original article link: http://www.raywenderlich.com/2712/using-properties-in-objective-c-tutorial
Tutorial:
This is the third tutorial on memory management using objc on the iPhone.
In the first tutorial, we introduced how to use instance variables and reference counts to manage memory in objective-C.
In the second tutorial, we will show you how to detect memory leaks and memory-related mistakes by using instruments and other auxiliary tools.
The third tutorial is the last tutorial in this series. Let's talk about the property of objc. We will introduce what a property is, how it works, what rules it has, and how it can be used to avoid most memory-related problems.
If you do not have a sample project for this series of tutorials, click here to download it. We will start from this project.
Retain your memory
Let's review where the memory needs to be managed in this project.
Currently, rootviewcontroller has two instance variables: _ sushitypes and _ lastsushiselected.
@interface RootViewController : UITableViewController {
NSArray * _sushiTypes;
NSString * _lastSushiSelected;
}
@end
Copy code
For _ sushitypes, we created it through alloc/init in viewdidload, and then release in viewdidunload and dealloc.
// In viewDidLoad. Afterwards, retain count is 1.
_sushiTypes = [[NSArray alloc] initWithObjects:@"California Roll",
@"Tuna Roll", @"Salmon Roll", @"Unagi Roll",
@"Philadelphia Roll", @"Rainbow Roll",
@"Vegetable Roll", @"Spider Roll",
@"Shrimp Tempura Roll", @"Cucumber Roll",
@"Yellowtail Roll", @"Spicy Tuna Roll",
@"Avocado Roll", @"Scallop Roll",
nil];
// In viewDidUnload and dealloc. Afterwards, retain count is 0.
[_sushiTypes release];
_sushiTypes = nil;
Copy code
For _ lastsushiselected, it is assigned a value when the user selects a row of table view. It has release in two places. One is before the value assignment, and the other is at dealloc. Please refer to the following code:
[_lastSushiSelected release];
_lastSushiSelected = [sushiString retain];
// In dealloc
[_sushiTypes release];
_sushiTypes = nil;
Copy code
This method is certainly feasible, but it requires you to seriously think about it. Every time you assign a value to a variable, you must seriously consider the memory problems related to it. Do you want to release first and then assign values? Do you want to retain? In short, when there are more variables and a large project, various memory problems will arise.
So next, I will introduce you to a simple method-using property to manage the memory.
Move the chair and start coding.
If you are familiar with other programming languages, such as Java or C #, you are certainly familiar with getters and setters. When you have an instance variable of _ sushitypes, you often need to allow other class objects to access this variable. However, it is not good to directly use the. Sign method to access it. It breaks the encapsulation principle and exposes the implementation of the class to the outside, as the programming master said. Believe it or not, I believe it. :)
Therefore, you need a method called "getsushitypes" (or just "sushitypes", which requires less than three letters). At the same time, you also need a method called "setsushitypes ". you can use these two methods to modify the instance variables of the category class. This is a good coding habit, because you can change the name of the instance variable, but you won't affect other classes, because the interface hasn't changed. Therefore, when coding code, we also need to encode more interface-specific code than implementation-specific code. Of course, there are other advantages to using getter and setter. You can use nslog to output some content in it so that you can know if anyone wants to snoop on your private variables. It is equivalent to a bodyguard.
As I said above, define the corresponding getter and setter methods for each instance variable. Of course, the premise is that you want to allow external access to this variable, don't make all the variables public. What is the meaning of encapsulation? In this way, memory management will become easier. Next, let's take a look at how I add getters and setters to these two variables.
First, declare the following four methods in rootviewcontroller. h:
- (NSArray *)sushiTypes;
- (void)setSushiTypes:(NSArray *)sushiTypes;
- (NSString *)lastSushiSelected;
- (void)setLastSushiSelected:(NSString *)lastSushiSelected;
Copy code
Then, add its implementation at the bottom of rootviewcontroller. M:
- (NSArray *)sushiTypes {
return _sushiTypes;
}
- (void)setSushiTypes:(NSArray *)sushiTypes {
[sushiTypes retain];
[_sushiTypes release];
_sushiTypes = sushiTypes;
}
- (NSString *)lastSushiSelected {
return _lastSushiSelected;
}
- (void)setLastSushiSelected:(NSString *)lastSushiSelected {
[lastSushiSelected retain];
[_lastSushiSelected release];
_lastSushiSelected = lastSushiSelected;
}
Copy code
The getter method here is very simple. They only return their respective variables.
The setter method first adds 1 to the input parameter reference count, reduces the previous instance variable reference count by 1, and then assigns the input variable to the instance variable. (TRANSLATOR: The writing method here is actually not good. I didn't consider the situation of self-assignment. If you have also passed the C ++ string class, you must consider the situation of self-assignment when writing the copy constructor and the value assignment operator. Otherwise, the problem may occur. However, there is no problem with the writing of the author above. Because it retakes first and then release. If you have reversed the statement and release it first, a problem occurs. However, if I consider self-assignment, I don't need to consider this order. For details about the writing method, refer to my original article, objc @ property ). In this way, the new parameter is referenced by the instance variable. Because it is the owner, it complies with the principle of "Who owns, who retain.
You may wonder why the setter method first calls retain/release and then assigns values, and the sequence cannot be changed. Of course, it must be to prevent auto-assignment. If you still don't understand it, forget it. You will understand some things one day. :)
Note: Why do we need to underline the name of the instance variable? In this way, the parameter names of the getter and setter methods can be conveniently obtained. If our instance variable is named "sushitypes", the parameter name of our setsushitypes function cannot be "sushitypes", because it will cause a conflict and an error will be reported during compilation and translation. At the same time, if you underline all the instance variables, your colleagues will immediately know that this is an instance variable. I have to be careful when using it. Of course, there are also Apple's KVC and KVO mechanisms, which also rely on underlines to search for keys. I will not talk about them here. Read the book.
Finally, note that the getter and setter methods here are not thread-safe. However, for this application, the getter and setter methods will only be accessed in the main thread, so "thread security is not secure", it's okay with us!
Now, you have a foundation. Open it!
Now you have new getter and setter. modify other code in this class and start using getter and setter. Let's start with sushitypes:
// In viewDidLoad
self.sushiTypes = [[[NSArray alloc] initWithObjects:@"California Roll",
@"Tuna Roll", @"Salmon Roll", @"Unagi Roll",
@"Philadelphia Roll", @"Rainbow Roll",
@"Vegetable Roll", @"Spider Roll",
@"Shrimp Tempura Roll", @"Cucumber Roll",
@"Yellowtail Roll", @"Spicy Tuna Roll",
@"Avocado Roll", @"Scallop Roll",
nil] autorelease];
// In viewDidUnload and dealloc
self.sushiTypes = nil;
Copy code
Call "self. sushitypes = xxx "and call" [self setsushitypes: XXX] ", which are completely equivalent -- here". for me, it's just "nice.
Therefore, instead of directly accessing the _ sushitypes instance variable, we use setter to set its value. In retrospect, setter will add 1 to the input logarithm reference count. therefore, now we cannot directly assign the value of alloc/init to _ sushitypes (because when we access through setter, the reference count of the variable created by alloc/init will be 2, which will cause a problem, because it has only one owner, which means that it will only be release once by the owner in the future. However, the reference count is still 1 and will never be released. Congratulations! Memory leakage !) Therefore, after calling alloc/init, we need to call autorelease.
In the viewdidunload and dealloc methods, we no longer manually set relase to nil. We only need to use setter to design self. xxx = nil. If you use self. _ sushitypes = nil, the following code is generated:
[nil retain]; // Does nothing
[_sushiTypes release];
_sushiTypes = nil;
Copy code
By the way, I 'd like to remind you that some people may say, "Never use the getter or setter method in the init or dealloc method ". Why are they saying this? Because if you use setter or getter in the alloc or dealloc functions, but its subclass overrides getter and setter, because all objc methods are "Virtual Methods ", that is to say, it can be rewritten. When the subclass init method calls [(Self = [Super init]), the init method of the parent class is called first, and getter and setter are used in it, these two methods are covered by you again. If you have done something in these two methods, then there will be a problem. Think about it, why! Because your subclass has not been fully implemented yet !!! You are still calling the "constructor" of the parent class, but you have used the subclass Method !!! However, what I want to say is that I am here in violation of the principles provided by some people. Why, because I know there may be side effects. Therefore, I will not easily reload the getter or setter methods of the parent class. This can simplify the code. This is of course my personal opinion, for your reference only.
Now, modify the code so that lastsushiselected can also use the setter method to assign values:
self.lastSushiSelected = sushiString;
// In dealloc
self.lastSushiSelected = nil;
Copy code
Wow --- now, We have less worries about memory problems, don't we? You don't have to worry about where you need retain or release. The setter method is used to manage the memory to some extent.
A simple suggestion
Therefore, writing getter and setter can facilitate other classes to access instance variables in your class, and sometimes make your memory management easier.
However, if you write a bunch of getter and setter methods over and over again, I will go crazy. Why is Java not crazy? Because eclipse can automatically generate. Why is C ++ not crazy? Because public can be used directly. However, objc @ public is useless. Don't worry, no one will think about repeated things over and over again, so objc2.0 provides a new and useful feature called @ property, also called attribute.
We can comment out all the getter and setter methods we wrote earlier. Just write the following two lines of code. Open rootviewcontroller. H, find the location stated by getter and setter, and replace it with the following two lines of code:
@property (nonatomic, retain) NSArray * sushiTypes;
@property (nonatomic, retain) NSString * lastSushiSelected;
Copy code
This is the first step to use attributes. Create an attribute declaration.
The attribute Declaration starts with the @ property keyword, and some parameters (such as atomic/nonatomic/assign/copy/retain) are input in parentheses ). Finally, you specify the attribute type and name.
Propetyceov is a special keyword that tells the compiler how to generate getter and setter. Here, you specify two parameters. One is nonatomic, which tells the compiler that you don't have to worry about multithreading. Another is reatin, which tells the compiler to retain the parameter before passing the setter parameter to the instance variable.
In other cases, you may want to use the "Assign" parameter instead of the reatin parameter. "Assign" tells the compiler not to retain the input parameter. Or, you need to specify the "copy" parameter. It will copy the parameter before the setter parameter is assigned to the instance variable.
Now, to use the property, go to rootviewcontroller. M, delete the previously written getter and setter, and add the following two lines of code at the top of the file:
@synthesize sushiTypes = _sushiTypes;
@synthesize lastSushiSelected = _lastSushiSelected;
Copy code
The code above tells the compiler that you should generate the corresponding getter and setter methods for me based on the property and its parameters defined above. You start with the @ synthesize keyword and then give the attribute name (if the attribute name is different from the instance variable name), you must write the = to sign so that when the setter method is generated, the compiler knows who the input parameter is assigned. Remember to write an equal sign at a time !!! If the instance variable name is the same as the attribute name, it is unnecessary.
That's all! Compile and run the Code. The Code is OK and runs well. However, compared with the heap of code you wrote earlier, is it easier to understand? It also reduces the probability of errors.
So far, you have used propety and how it works! Next, I will provide some suggestions for using property.
General Strategy
I want to add some such policies in this tutorial, because it can help me manage the objc memory more easily and less easily.
If you follow these rules, you will stay away from memory-related problems in most cases. Of course, if you blindly stick to these rules without understanding them, why are these rules useful. This is definitely not the case! However, if you are a newbie, you can follow these rules, so that you will avoid a lot of memory-related errors and make your daily programming activities easier.
I will first list these rules, and then I will discuss them in detail.
- Always define attributes for all instance variables.
- If it is a class, set "retain" as the attribute parameter. Otherwise, set it to assign.
- Use alloc/init/autorelease to create a class instance at any time.
- Always use e "self. xxx = yyy" when assigning values to a variable ". In other words, property is used.
- For each of your instance variables, call "self. xxx = nil" in the dealloc function ". If it is an outlet, create it in viewdidload. Remember to destroy it in viewdidunload.
Okay. Now let's discuss it one by one!
Rule 1: by defining the property for each instance variable, you can let the compiler write memory-related code for you. The disadvantage is obvious. You break the encapsulation of the class. In this way, the coupling degree of your class may be higher, and maintenance and code reuse may not be used.
Rule 2: by specifying the property parameter of the class as retain, you can access them at any time. You also save a reference count for them, and the memory will not be released. You are the owner and you are responsible for releasing the memory.
Rule 3: When you create a class object, use the alloc/init/autorelease usage (just as if you created an array of sushitypes ). In this way, the memory will be automatically released. If you want to keep it from being released, declare a retain in the instance variable to be assigned a value and its property parameter.
Rule 4: Use self whenever you assign values to instance variables. xxx syntax. In this way, when you assign values to instance variables, the old value will be released first and the new variable value will be retain. Note: Some programmers are worried that using the getter and setter functions in the init and dealloc functions will cause side effects. However, I don't think this is anything. As long as you fully understand the memory management rules, you will not do the "override the getter and setter of the parent class in the subclass" method. Or, even if you need to rewrite the getter and setter of the parent class, you will also pay attention that it will not bring any side effects to the Code during the rewriting process, right?
Rule 5: Use "self. xxx = nil" in the dealloc function, so that you can use the property function to reduce the reference count by 1. Do not forget that there is viewdidunload!
Simple cocos2d-related policies
I know that my blog now has a large number of cocos2d loyal fans. Therefore, the following tips is specially prepared for you!
The five rules proposed above are a little too strict for coocs2d, or simply say, they are too dead. Most of the time, our objects are added to the layer. We define some instance variables in the class just to use methods in methods other than the init method. (In fact, many people like to define tags, specify a tag when addchild is used, and use [self getchildbytag: XXX] in other methods to get the desired object. Because there is a ccarray array in the layer, it is used to store all child nodes in the layer. When addchild is called, The addobject method of ccarray is actually called. Therefore, the child added to the layer is, the reference count will add 1.
Therefore, in order to avoid defining unnecessary properties, the following are some suggestions for cocos2d users:
- Never use property.
- Assign the sprite instance you created to the instance variable you defined.
- Because these genie will be added to the current layer, coocs2d will automatically retain, so that its reference count is increased by 1.
- When you remove an object from the current layer, remember to assign it to nil.
I personally think that using the above four methods to develop cocos2d games is not bad, simple, and fast.
Note: if an object is not added to the current layer, such as action. Then, the above four rules will not be used. Go to reatin/release manually.
Remember, rules are dead and people are active! As long as you understand the objc memory management rules, you can forget all the above rules!
Where to go?
The complete source code of this tutorial is provided here.
If you have any questions about property or memory management, leave a message. Of course, if you have any good ideas about memory management and tips, please share them with us. Thank you very much!
So far, all the tutorials on the objc memory management series have ended. I sincerely hope that by translating these three tutorials and the one I wrote myself, we can help you get out of the objc memory management quagmire.
If you have any good comments or suggestions, leave a message below. If you want to get some tutorials or you are not familiar with them, leave a message. Although I am very busy (in fact everyone is very busy), but when I have time, I will try my best to meet your requirements.