Welcome to follow my official account. I will regularly share some solutions to the problems I encounter in the project and some practical skills of iOS. At the present stage, I will mainly sort out some basic knowledge and record it

The post will also be synchronized to my blog: ppsheep.com

I have read Effective Objective-C 2.0 before, and I decided to read it again. I think it will be a different experience. Here I will record the process of reading Effective Objective-C 2.0 for your reference.

Minimize references to other header files in class header files

OC, like C and C++, uses header files (.h) and implementation files (.m) to separate code. The code looks like this:

//PPSTeacher.h
#import <Foundation/Foundation.h>

@interface PPSTeacher : NSObject
@property (nonatomic.copy) NSString *name;
@end


//PPSTeacher.m
#import "PPSTeacher.h"

@implementation PPSTeacher
// Implement it
@endCopy the code

Every class in OC needs to import foundation.h. If you don’t refer to it in the class itself, then you need to refer to the basic header file that is corresponding to its superclass. For example, our common UIViewController usually subclasses that inherit UIViewControlelr need to introduce UIKit.h because most of UIKit is used in every control that you use, Foundation.h has already been introduced in the controls that you need to use, so you’re actually introducing the Foundatiuon framework

Ok, so now we need to create another student class, PPSStudent, one student per teacher, so maybe our code will do that

#import <Foundation/Foundation.h>
#import "PPSStudent.h"

@interface PPSTeacher : NSObject

@property (nonatomic.copy) NSString *name;
@property (nonatomic.strong) PPSStudent *student;

@endCopy the code

Of course there will be no problem, but let’s think about it, in Teacher, I only need to know that there is such a class of students, I don’t want to know what he can do, I don’t care. OC provides this approach, called “forward declaration” :

@class PPSStudent;

#import <Foundation/Foundation.h>

@class PPSStudent;
@interface PPSTeacher : NSObject

@property (nonatomic.copy) NSString *name;
@property (nonatomic.strong) PPSStudent *student;

@endCopy the code

In the implementation file we need to introduce #import “ppsstudent.h” because during the implementation we need to know what PPSStudent can do

#import "PPSTeacher.h"
#import "PPSStudent.h"

@implementation PPSTeacher
/ / implementation
@endCopy the code

There are two advantages to this:

  • Can shorten the compiler compile time
  • Circular references can also be avoided

Avoid circular references:

It used to be that each teacher had one student, and we added logic that each student needed one teacher

If you follow the previous direct #import approach

PPSTeacher.h

#import <Foundation/Foundation.h>
#import "PPSStudent.h"

@interface PPSTeacher : NSObject

@property (nonatomic.copy) NSString *name;
@property (nonatomic.strong) PPSStudent *student;

@endCopy the code

PPSStudent.h

#import <Foundation/Foundation.h>
#import "PPSTeacher.h"

@interface PPSStudent : NSObject

@property (nonatomic.copy) NSString *name;
@property (nonatomic.copy) NSString *name;

- (void)addTeacher:(PPSTeacher *)teacher;

@endCopy the code

If we had referenced it this way then we would have circular references, we would have gotten an error from compilation because when we compile PPSStudent we import PPSTeacher, and then the compiler compiles PPSTeacher, and when we compile PPSTeacher, we find PPSStudent, This creates a reference loop.

Use more words than their equivalents

Using literals makes code concise and readable

Literal value

The conventional approach is what we need

NSNumber *number = [NSNumber numberWithInt:1];Copy the code

With the literals, we just need to

NSNumber *number = @1;Copy the code

And all data types represented by NSNumber instances can use literals:

NSNumber *number = @1;
NSNumber *number = @2.5f;
NSNumber *number = @23.312121;
NSNumber *number = @YES;
NSNumber *number = @'a';Copy the code

Literal array

The usual way to create an array is:

NSArray *arr = [NSArray arrayWithObjects:@ "1".@ "2".nil];Copy the code

Use literals

NSArray *arr = @[@ "1".@ "2"];Copy the code

Literal dictionary

General creation method:

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"key1".@"object1".@"key2".@"object2".nil];Copy the code

This can lead to confusion, as key and value are completely indistinguishable

Use literals:

NSDictionary *dic = @{
                          @"key1"   :   @"value1".@"key2"   :   @"value2"};Copy the code

Use literal bullet points

  • Strings, numbers, arrays, and dictionaries should be created as literals as possible
  • To access arrays or dictionaries, use subscripts such as arr[1] dic[@”key1″]
  • When creating a literal, you need to make sure there are no nil objects in the value, otherwise an exception will be raised

Use type constants more often than #define preprocessors

Let’s start with constants that don’t need to be open to the outside world

When writing code, we often define constants. For example, if you want to write a UIView view class, the view displays and starts playing an animation, and then disappears. So we might want to extract the running time of this animation as a constant, and in general, we would write it like this:

It’s defined in dot m, of course, because it doesn’t have to be open

# define ANIMATION_DURATION 0.3Copy the code

The downside of this definition is that we don’t know what type of constant this is or what it actually does. There’s a better way to do this than preprocessing, right

static const NSTimeInterval kAnimationDuration = 0.3;Copy the code

Constants defined in this way, which indicate that they are of type NSTimeInterval, help the rest of the team understand the code, and help with development documentation. The more constant definitions there are, the more advantage this method has

For constant names, the general usage is:

If the constant only applies to the current compilation unit (that is, the current.m implementation class), then you should prefix the name of the constant with k

If the constant is also external, it needs to be prefixed with the current class name

A constant must be defined with both static and const because we expect it to be a constant that cannot be changed

Another reason is that our constants only apply to the current.m class. If we do not add static, the compiler will add an external symbol to our current class. If another class defines a variable with the same name, the compiler will report an error

Constants that need to be open to the outside world

In this case, we usually see that an operation needs to be done in the current class and we need to send a global notification Center (NSNotificationCenter) to notify others. When we send the notification, we need to use the current constant string, and externally, the receiver needs to know about such a string

We usually define it this way

In the header file :(assuming the current class name is PPSView)

extern NSString *const PPSViewNotofication;Copy the code

In the implementation file

NSString *const PPSViewNotofication = @"PPSViewNotofication";Copy the code

In this way, when the compiler knows the extern keyword in the imported header file, it will know that it needs a PPSViewNotofication symbol in the global symbol table. The compiler does not need to know the definition of this symbol. When linked to the binary file, it will find the constant.

Try to access instance variables directly from within the object

Outside of objects, we know that instance variables are always operated on via properties, but what about inside an instance? It is strongly recommended that in all cases except lazy loading, it should be:

Variables should be read in the form of direct access (_ variable name), and set by properties when instance variables are set

Let’s look at an example:

There is currently a PPSPerson class

//PPSPerson.h
@interface PPSPerson : NSObject

@property (nonatomic.copy) NSString *firstName;
@property (nonatomic.copy) NSString *lastName;

- (NSString *)fullName;

- (void)setFullName:(NSString *)funllName;

@end

//PPSPerson.m
@implementation PPSPerson- (NSString *)fullName{
    return [NSString stringWithFormat:@ % @ % @ "".self.firstName,self.lastName]; } - (void)setFullName:(NSString *)funllName{
    NSArray *components = [funllName componentsSeparatedByString:@" "];// Separate firstName from lastName with a space
    self.firstName = components[0];
    self.lastName = components[1];
}

@endCopy the code

In the above code, we used point syntax to access the associated instance variables. Now suppose we access the instance variables directly without accessing them:

@implementation PPSPerson- (NSString *)fullName{
    return [NSString stringWithFormat:@ % @ % @ "",_firstName,_lastName]; } - (void)setFullName:(NSString *)funllName{
    NSArray *components = [funllName componentsSeparatedByString:@" "];// Separate firstName from lastName with a space
    _firstName = components[0];
    _lastName = components[1];
}

@endCopy the code

There are several differences between these two ways of writing:

  • It’s certainly faster to access instance variables directly without going through Objective-C’s “method dispatch” (more on that later). The code generated by the compiler directly accesses the chunk of memory that holds the object instance variable
  • Instance variables are accessed directly without their “set methods” being called, bypassing the “memory management semantics” defined for related properties. For example, if you access a property declared as copy directly under ARC, the property is not copied, only the new value is whittled out and the old value is released
  • KVO will not be triggered if accessed directly, depending on the requirements
  • Property access helps troubleshoot errors associated with it, and breakpoint debugging can be added to the set and GET methods

The lazy loading method described earlier must be accessed using properties, otherwise the instance variable will never be initialized

The main points of

  • When reading data from within an object, it should be read directly through instance variables, and when writing data, it should be written through properties
  • In initialization and dealloc methods, data should always be read and written through instance variables
  • Data should be read by properties in lazy loading