First, the class
1. Class name
class names should be prefixed with three uppercase letters (two-letter prefix for Apple's class reservation)
Not only the class, the public constants, protocol, etc. are prefixed by the same three uppercase letters.
When you create a subclass, you should place the descriptive part in the middle of the prefix and the parent class name.
For example:
If you have a Zocnetworkclient class, the name of the subclass will be zoctwitternetworkclient (note "Twitter" between "Zoc" and "networkclient"); According to this convention, a uiviewcontroller subclass would be zoctimelineviewcontroller.
2. Initializer and Dealloc
The recommended code is organized by placing the Dealloc method at the front of the implementation file (directly after @synthesize and @dynamic), and Init should follow the Dealloc method.
If there are multiple initialization methods, the specified initialization method should be placed at the front and the indirect initialization method follows.
There is little need to implement the Arc,dealloc method today, but to put Init and dealloc together, emphasize that they are a pair. What is usually done in the Init method needs to be undone in the Dealloc method.
About specifying initialization methods (designated initializer) and indirect initialization methods (secondary initializer)
Objective-c has the idea of specifying initialization methods (designated initializer) and indirect (secondary initializer) initialization methods. The designated initialization method is to provide all parameters, the secondary initialization method is one or more, and provide one or more default parameters to invoke the initialization method of the designated initialization.
Copy Code code as follows:
@implementation Zocevent
-(Instancetype) Initwithtitle: (NSString *) title
Date: (NSDate *) Date
Location: (cllocation *) location
{
self = [super init];
if (self) {
_title = title;
_date = date;
_location = location;
}
return self;
}
-(Instancetype) Initwithtitle: (NSString *) title
Date: (NSDate *) Date
{
return [self initwithtitle:title date:date location:nil];
}
-(Instancetype) Initwithtitle: (NSString *) title
{
return [self initwithtitle:title date:[nsdate Date] location:nil];
}
@end
InitWithTitle:date:location: Is the designated initialization method, the other two is the secondary initialization method. Because they are simply designated initialization methods that invoke the class implementation.
A class should have and only one designated initialization method, and other initialization methods should call this designated initialization method (with the exception).
3. There are three different ways to define a new class:
(1) You do not need to overload any of the initialization functions
(2) Heavy duty designated initializer
(3) Define a new designated initializer
The first approach does not require any initialization logic for the class, which means that the initialization method of the parent class does not have to be overridden in the class, and no other action is required.
The second way is to overload the specified initialization method of the parent class. Example:
Copy Code code as follows:
@implementation Zocviewcontroller
-(ID) Initwithnibname: (NSString *) Nibnameornil Bundle: (NSBundle *) Nibbundleornil
{
Call to the superclass designated initializer
self = [super Initwithnibname:nibnameornil Bundle:nibbundleornil];
if (self) {
Custom initialization (customized initialization process)
}
return self;
}
@end
In this example, Zocviewcontroller inherits from Uiviewcontroller, where we have some other requirements (such as assigning values to some member variables at the time of initialization), so we need to override the specified initialization method of the parent class Initwithnibname: Bundle: Method.
Note that if you do not overload this method here and overload the Init method of the parent class, it would be an error.
Because when you create this class (Zocviewcontroller), you call Initwithnib:bundle: This method, so we overload this method to first ensure that the parent class is initialized successfully and then perform additional initialization in this method. However, if you overload the Init method, the Init method (called Initwithnib:bundle: This specifies the initialization method) is not invoked when the class is created.
The third way is to provide your own class initialization method, and you should follow these three steps to ensure correctness:
Define your designated initializer and make sure that you invoke the designated initializer of the direct superclass.
Overloaded designated initializer for direct Super class. Call your new designated initializer.
Write a document for the new designated initializer.
Many developers ignore the next two steps, which are not just a careless question, but also violate the rules of the framework and can lead to uncertain behavior and bugs.
The right example:
Copy Code code as follows:
@implementation Zocnewsviewcontroller
-(ID) initwithnews: (Zocnews *) News
{
Call to the immediate superclass ' s designated initializer (Invoke direct superclass designated initializer)
self = [super Initwithnibname:nil Bundle:nil];
if (self) {
_news = news;
}
return self;
}
Override the immediate superclass ' s designated initializer (overloaded designated initializer for direct parent classes)
-(ID) Initwithnibname: (NSString *) Nibnameornil Bundle: (NSBundle *) Nibbundleornil
{
Call the new designated initializer
return [self initwithnews:nil];
}
@end
Many developers write only the first custom initialization method, without overloading the parent class's specified initialization method.
In the first custom initialization method, because we want to define our own specified initialization method, we start by invoking the specified initialization method of the parent class to ensure that the parent class is initialized successfully, so that Zocnewsviewcontroller is the available state. (Because the parent class is created through the Initwithnibname:bundle: This specifies the initialization method, we call this method of the parent class to ensure that the parent class is initialized successfully). Then assign a value to _news later.
There is a problem if this is the only way to do it. It is perfectly legal for the caller to initialize this class if it is called Initwithnibname:bundle: if this is the case, then Initwithnews: This method will never be invoked, so _news = news will not be executed, This results in an incorrect initialization process.
The workaround is to overload the specified initialization method of the parent class, returning the new specified initialization method in this method, as in the example, so that any method invoked can be initialized successfully.
An indirect initialization method is a method that provides default values, behavior, and initialization methods.
You should not have initialization instance variables in the indirect initialization method, and you should always assume that this method will not be invoked. We guarantee that the only way to be called is designated initializer.
This means that your secondary initializer should always call designated initializer or you customize (the third case above: custom designated initializer) of self designated Initializer. Sometimes, because of the error, it is possible to play super, which causes the initialization order that does not conform to the above mentioned.
That is, you may see that a class has multiple initialization methods, actually a specified initialization method (or several, such as Uitableviewcontroller) + multiple indirect initialization methods. These concise initialization methods may do different things depending on the parameters, but essentially invoke the specified initialization method. Therefore, the indirect initialization method may not be invoked, but the specified initialization method is invoked (not every one will be called, but the last call must be a specified initialization method). (This can be extended to the issues mentioned above, we can override the specified initialization method of the parent class directly, or we can customize the initialization method (the code in this method requires the form of self = [Super Parent initialization Method]), and if it is a custom initialization method, You should also override the initialization method inherited from the parent class to return our custom initialization method ... )。
In short, if you override the specified initialization method of the parent class, you first need to invoke the corresponding initialization method of the parent class; If you increase the custom specified initialization method, first call the corresponding initialization method of the parent class in the new custom specified initialization method, and then you need to override the specified initialization method of the parent class. Invokes the custom specified initialization method that you just added in the overridden method.
4. Supplementing
A class may have more than one specified initialization method, or it may have only one specified initialization method.
Taking Uitableviewcontroller as an example, we can see:
Copy Code code as follows:
-(Instancetype) Initwithstyle: (uitableviewstyle) style ns_designated_initializer;
-(Instancetype) Initwithnibname: (NSString *) Nibnameornil Bundle: (NSBundle *) Nibbundleornil ns_designated_ initializer;
-(Instancetype) Initwithcoder: (Nscoder *) Adecoder Ns_designated_initializer;
It has three specified initialization methods, and we have just said that when subclasses inherit from the parent class and override the initialization method, you first need to invoke the initialization method of the parent class, but if there are multiple initialization methods for a class, which one do you need to call?
In fact, different ways of creating a different method of specifying initialization are invoked.
For example, we create uitableviewcontroller in the form of nib, then the last Call is-(instancetype) Initwithnibname: (nsstring) Nibnameornil Bundle: ( NSBundle) Nibbundleornil This specified initialization method, and if we create in storyboard form, then the last Call is-(instancetype) Initwithcoder: (Nscoder *) Adecoder This specifies the initialization method. If created in the form of code, then the final invocation is-(instancetype) Initwithstyle: (Uitableviewstyle) style, the specified initialization method. So different situations need to rewrite different initialization methods, and when overridden, first invoke the corresponding initialization method of the parent class (such as overriding Initwithcoder: method, then first self = [super Initwithcoder: ...], all one by one corresponding).
Again, for example, we create the Uiviewcontroller in the form of nib, then the last Call-(instancetype) Initwithnibname: (nsstring) Uiviewcontroller Nibnameornil Bundle: (NSBundle) Nibbundleornil, this is the same as uitableviewcontroller; if we create in the form of storyboard, then the last Call is-( Instancetype) Initwithcoder: (Nscoder) Adecoder, this is the same as uitableviewcontroller; but if we create Uiviewcontroller in code (eg: Cylviewcontroller VC = [[Cylviewcontroller alloc] init]; Cylviewcontroller inherits from Uiviewcontroller), then it finally invokes the actual-(Instancetype) Initwithnibname: (nsstring) Nibnameornil Bundle: ( NSBundle) Nibbundleornil, this is not the same as Uitableviewcontroller, because Uiviewcontroller does not-(instancetype) Initwithstyle: ( Uitableviewstyle) style, so when created with code, the last call is Initwithnibname:bundle this specified initialization method, and the parameter is automatically set to nil.
So now look back at Uitableviewcontroller, when you're creating it using code (Eg:cyltableviewcontroller TVC = [[Cyltableviewcontroller alloc] init] ; or Cyltableviewcontroller TVC = [[Cyltableviewcontroller alloc] initwithstyle:uitableviewstyleplain]; It will call Initwithstyle: This method, but if you also implement the Initwithnibname:bundle: This method, you will find that this method is also called. Because Uitableviewcontroller inherits from Uiviewcontroller, when you create it in code, you end up with Initwithnibname:bundle: (because that's what Uiviewcontroller did).
So when you create Uitableviewcontroller in code, it calls Initwithnibname:bundle: and Initwithstyle: These two methods.
Second, the property
attributes are named as descriptive as possible, and are named with the hump.
About the location of "*":
Copy Code code as follows:
Recommended
NSString *text;
Not recommended
nsstring* text;
NSString * TEXT;
Note that this habit and constant are different.
Static NSString * Const ...
You can never use getter and setter methods in init (and other initialization functions), you should access the instance variables directly. Remember that an object is considered initialized to a state only when it is returned by Init.
Use the dot notation as much as possible when using the Setter/getter method.
Copy Code code as follows:
Recommended
View.backgroundcolor = [Uicolor Orangecolor];
[UIApplication sharedapplication].delegate;
Not recommended
[View Setbackgroundcolor:[uicolor Orangecolor]];
UIApplication.sharedApplication.delegate;
Using dot notation makes the expression clearer and helps distinguish between attribute access and method invocation.
Attribute definition
Copy Code code as follows:
@property (nonatomic, ReadWrite, copy) NSString *name;
Attributes should be listed in this order: atomicity, read-write, and memory management.
When you are accustomed to modifying modifiers on a property, you typically search for modifiers that need to be fixed from the property name from right to left. Modifiers on these properties are most likely to be modified from the far right, and the likelihood that these modifiers will be modified based on experience from the high end should be: Memory management > read-write Permissions > Atomic operations
You must use nonatomic unless the situation is particularly needed. In iOS, locks brought by atomic particularly affect performance.
If you want a public getter and private setter, you should declare that the exposed property is readonly and that the generic attribute is always redefined in the class extension as ReadWrite.
Copy Code code as follows:
In the. h file
@interface Myclass:nsobject
@property (nonatomic, ReadOnly, strong) NSObject *object;
@end
Copy Code code as follows:
In the. m file
@interface MyClass ()
@property (nonatomic, ReadWrite, strong) NSObject *object;
@end
Copy Code code as follows:
@implementation MyClass
Do something cool
@end
If a word describing the bool attribute is an adjective, the setter should not have the is prefix, but its corresponding getter accessor should take this prefix.
@property (Assign, getter=iseditable) BOOL editable;
Any memory management type that can be used to set the properties of a variable object (such as nsstring,nsarray,nsurlrequest) must be copy. (This is what the original text says, but what I understand is not absolute.) If you do not want the original Mutable object to affect the corresponding property of the class, then you need to use copy, so that when you assign a value, the variable object will first copy the deep copy, and then assign the copied value to the property of the class, which will ensure that the class attribute and the original variable object influence do not affect. But if you want the class attribute to be a strong reference to the original Mutable object, point to the Mutable object, then use strong. )
You should also avoid exposing objects that are mutable in the exposed interface, as this allows your class's users to change their own internal representations and destroy the encapsulation of the class. You can provide properties that can be read-only to return the immutable copy of your object.
Copy Code code as follows:
/*. h */
@property (nonatomic, readonly) Nsarray *elements
/*. m */
-(Nsarray *) elements {
return [self.mutableelements copy];
}
Although using lazy loading is good in some cases, it should be considered before use, because lazy loading usually produces some side effects. (But lazy loading is more commonly used, such as the following example)
Side effects mean that when a function is called, it also has an additional effect on the main calling function, in addition to returning the function value. For example, modify a global variable (a variable outside of a function) or modify a parameter. The function side effect will cause unnecessary trouble to the program design, bring the procedure very difficult to find the error, and reduce the readability of the program.
Copy Code code as follows:
-(NSDateFormatter *) Dateformatter {
if (!_dateformatter) {
_dateformatter = [[NSDateFormatter alloc] init];
Nslocale *enusposixlocale = [[Nslocale alloc] initwithlocaleidentifier:@ "En_us_posix"];
[_dateformatter Setlocale:enusposixlocale];
[_dateformatter setdateformat:@ ' yyyy-mm-dd ' T ' HH:mm:ss. SSS "];//milliseconds are SSS, not sssss
}
return _dateformatter;
}
third, the method
1. Parameter Assertion
Your method may require some parameters to meet certain conditions (such as not nil), in which case it is best to use Nsparameterassert () to assert whether the condition is valid or to throw an exception.
Copy Code code as follows:
-(void) viewdidload
{
[Super Viewdidload];
[Self testmethodwithaparameter:0];
}
-(void) Testmethodwithaparameter: (int) value
{
Nsparameterassert (value!= 0);
NSLog (@ "execute correctly");
}
In this case, if the passed argument is 0, the program throws an exception.
2. Private method
Never prefix your private method with an _. This prefix is reserved by Apple. Don't risk the private method of overloading the apple.
Remember this convention when you want to achieve equality: you need to implement both isequal and hash methods at the same time. If two objects are considered equal by isequal, their hash method needs to return the same value. But if the hash returns the same value, it does not guarantee that they are equal.
Copy Code code as follows:
@implementation Zocperson
-(BOOL) IsEqual: (ID) Object {
if (self = = object) {
return YES;
}
if (![ Object Iskindofclass:[zocperson class]] {
return NO;
}
Check objects properties (name and birthday) for equality (check the equality of object properties (names and birthdays)
...
return propertiesmatch;
}
-(Nsuinteger) hash {
return [self.name hash] ^ [self.birthday hash];
}
@end
You should always use isequalto< #class-NAME-WITHOUT-PREFIX#>: This format implements an equality checking method. If you do this, you will first call this method to avoid the type check above.
So a complete IsEqual method should be this:
Copy Code code as follows:
-(BOOL) IsEqual: (ID) Object {
if (self = = object) {
return YES;
}
if (![ Object Iskindofclass:[zocperson class]] {
return NO;
}
return [self Isequaltoperson: (Zocperson *) object];
}
-(BOOL) Isequaltoperson: (person *) person {
if (!person) {
return NO;
}
BOOL Namesmatch = (!self.name &&!person.name) | |
[Self.name IsEqualToString:person.name];
BOOL Birthdaysmatch = (!self.birthday &&!person.birthday) | |
[Self.birthday IsEqualToDate:person.birthday];
return haveequalnames && haveequalbirthdays;
}
Four, Category
Precede the category method with your own lowercase prefix and underline. (It's really ugly, but Apple recommends it too)
Copy Code code as follows:
-(ID) zoc_mycategorymethod
This is very necessary. Because if the same method name is already used in the extended category or other category, it can cause unpredictable consequences. (The last loaded method is called)
Copy Code code as follows:
Recommended
@interface NSDate (zoctimeextensions)
-(NSString *) Zoc_timeagoshort;
@end
Copy Code code as follows:
Not recommended
@interface NSDate (zoctimeextensions)
-(NSString *) Timeagoshort;
@end
It is recommended that you use category to group methods based on different functions.
Copy Code code as follows:
@interface Nsdate:nsobject <nscopying, nssecurecoding>
@property (readonly) Nstimeinterval timeintervalsincereferencedate;
@end
Copy Code code as follows:
@interface NSDate (nsdatecreation)
+ (instancetype) date;
+ (Instancetype) Datewithtimeintervalsincenow: (nstimeinterval) secs;
+ (Instancetype) Datewithtimeintervalsincereferencedate: (Nstimeinterval) ti;
+ (Instancetype) dateWithTimeIntervalSince1970: (nstimeinterval) secs;
+ (Instancetype) Datewithtimeinterval: (nstimeinterval) secstobeadded sincedate: (nsdate *) date;
// ...
@end
Five, Nsnotification
When you define your own nsnotification you should define the name of your notice as a string constant, just as you expose it to other string constants of other classes. You should declare it as extern in the exposed interface file and define it in the corresponding implementation file.
Because you expose the symbol in the header file, you should prefix the notification name with the class name prefix according to the uniform namespace prefix rule. (usually the externally supplied constants in the header file need to be prefixed, declared extern + const, not defined in the header file, but defined in the implementation file.) If it is not an externally exposed constant, it is usually declared as static + const directly in the implementation file and prefixed with a prefix, which is defined directly later. )
At the same time, it is also a good practice to name this notice using a did/will verb and a "notifications" suffix.
Copy Code code as follows:
Foo.h
extern NSString * Const zocfoodidbecomebarnotification
Foo.m
NSString * Const Zocfoodidbecomebarnotification = @ "Zocfoodidbecomebarnotification";