“Writing High Quality OC Code” has been successfully completed chapter one, two, three, four, five, six, eight! Attach a link: Objective-c code for iOS (1) — Objective C code for iOS (2) — Objective C code for iOS (3) — Interface and API design Objective-c code (5) — Memory management mechanism (6) — Block column iOS Writing high quality Objective-C code for iOS (GCD


In this article, we will introduce the interface and API design of OC to improve the quality of Objective-C code

Use prefixes to avoid namespace collisions

OC has no concept of a namespace. Therefore, we need to prefix the class to avoid duplication of names and avoid naming conflicts. Of course, not only class names, but also some global variables and methods need to be distinguished with appropriate prefixes.

So, we need to:

  • Select company – and project-related prefixes for the class names.

  • To avoid conflicts caused by repeated references to third-party libraries, prefix them if necessary.

2. Provide “universal initialization method”

  • Provide one in the classOmnipotent initialization method, and put a note in the document. All other initializers call this omnipotent initializer.
  • Benefit: When the structure of a class changes or the initialization logic changes, you only need to change the omnipotent initialization method.

For example, the NSDate class defines a universal initialization method:

- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
Copy the code

The rest of the initialization methods are defined in the NSDate (NSDateCreation) classification

- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
Copy the code

In the official documentation, If you want to subclass NSDate to obtain behavior different than that provided by the private or public subclasses, you must:

  • Override initWithTimeIntervalSinceReferenceDate:, one of the designated initializer methods

Select a universal initializer and all other initializers will call the universal initializer. The advantage of this is that later if the initialization logic changes, you only need to change the universal initializer, or even if the subclass overwrites the universal initializer

3. Implement description method

This article is written by overwriting description (or debugDescription) method to output more custom information during NSLog printing (or LLDB printing).

Here’s an example:

- (NSString *)description {

    return [NSString stringWithFormat:@"<%@: %p, %@>",
            [self class],
            self,
            @{
              @"qi": _qi,
              @"share" : _share}
            ];
}
Copy the code

Try to use immutable objects

  1. When declaring external properties, use immutable objects as much as possible, and add as much as possible to external property declarationsreadonlyModifier ~ (default is readwrite modifier) ~. In this way, the external can only read the data, not modify it, ensuring that the data held by instances of this class is more secure. In particular, mutable collections should not be exposed as attributes. Instead, methods should be provided to modify mutable collections. ~
  2. There are two ways to modify the value of an object:
    • provideInterface methodsModify the
    • useKVC (key-value Coding)Technique ~ This technique allows the data or properties of an object to be looked up at run time by their key names, where the name of the property is the key name of its value. In static languages, this is not possible. KVC adds a lot of design freedom: you can access an object’s properties or data without knowing its type. ~

Not recommended

//Animals.h
@property (nonatomic, strong) NSSet *animals;
Copy the code

Should be changed to:

//Animals.h
@interface Animals : NSObject

@property (nonatomic, strong, readonly) NSSet *animals;

- (void)addAnimal:(NSString *)animal;
- (void)removeAnimal:(NSString *)animal;

@end


//Animals.m
@implementation Animals {
    NSMutableSet *_mutableAnimals;
}

- (NSSet *)animals {
    return [_mutableAnimals copy];
}

- (void)addAnimal:(NSString *)animal {
    [_mutableAnimals addObject:animal];
}

- (void)removeAnimal:(NSString *)animal {
    [_mutableAnimals removeObject:animal];
}
Copy the code

However, xiaobian believes that it is good to write this way: to ensure the security of the data, but the amount of code will also improve a lot. Therefore, it is recommended that you can choose to use it for some important classes.

In addition, if a property can only be changed inside the object, it can be declared readonly in the.h file. Then extend the property to a readwrite property in the.m class extension.

You can also declare the property as readonly in the.h file, modify the value in the.m file with the instance variable, and when you modify the value inside the block, you can use the self-> instance variable method to access the change. ~ (Xiaobian test, indeed effective. Welcome to continue the discussion

Five, use clear and coordinated naming

Master’s quote: “Writing OC code is like telling a story, while reading OC code is more like listening to a story.”

This is thanks to OC’s clear and coordinated naming.

  • First, there is the hump of naming: this is the same as in most programming languages.
  • Secondly, it is also the most critical method naming. From left to right it reads like a sentence in everyday speech.

For example, we want to initialize a rectangle and assign its width and height.

// C++:
Rectangle *aRectangle = new Rectangle(5.0.10.0);

// Objective-C:
Rectangle *aRectangle = [[Rectangle alloc] initWithWidth:5.0 andHeight:10.0];
Copy the code

Obviously, the OC method can be very straightforward to see the specific meaning of the parameters to be passed, while C++ parameters are not so intuitive.

Prefix private method names

QiShare prefers to use #pragma Mark – to distinguish between public and private. Such as:

#pragma mark - Private Functions

// code...


#pragma mark - Action functions

// code...


#pragma mark - Request functions

// code...


#pragma mark - xxxDataSource

// code...


#pragma mark - xxxDelegate

// code...

Copy the code

Of course, you can also customize the specifications for your team.

Understand objective-C error models

Many languages have exception handling mechanisms, and Objective-C is no exception. If an exception is thrown in the OC, it may cause a memory leak. In the OC, Automatic Reference Counting is set to No exception security by default. Simply put, once an exception is thrown, the object cannot be released automatically. So,

  1. Exceptions are only used to handle fatal errors.
  2. For non-fatal errors, there are two solutions:
    • Let the object returnnilor0(for example: invalid initialization argument, method returns nil or 0)
    • useNSError

Understand the NSCopying agreement

In iOS development, it is often necessary to copy an object when working with it, which is done using the copy/mutableCopy method. If we want our classes to support copying, we must implement the NSCopying protocol, which has only one method:

- (id)copyWithZone:(nullable NSZone *)zone;
Copy the code

Of course, if you want to return objects that are mutable, you need to implement the NSMutableCopying protocol.

- (id)mutableCopyWithZone:(nullable NSZone *)zone;
Copy the code

At the same time, when copying objects, pay attention to whether to perform shallow copy or deep copy

Which brings up the idea: what is a deep copy? What is shallow copy?

  • Deep copy: Copy content (both new copiesPointer to theAnd they copy outThe new Object)
  • Shallow copy: pointer copy (only new ones are copiedPointer to thePoint to theThe original Object)

Here’s a classic illustration:

Deep copy
Shallow copy
[NSMutableArray copy]
NSArray
[NSArray mutableCopy]
NSMutableArray

Say too much, give a Demo~

  • Here is a Demo of the NSCopying protocol.

QiShareMember. H:

@interface QiShareMember : NSObject <NSCopying> @property (nonatomic, copy, readonly) NSString *name; / /! < name @property (nonatomic, copy, readonly) NSString *sex; / /! < gender @property (nonatomic, assign, readOnly) NSUInteger age; / /! Age < / /! Initialization method - (instancetype)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSUInteger)age; - (void)addFriend:(QiShareMember *)friend; - (void)removeFriend:(QiShareMember *)friend; @endCopy the code

QiShareMember. M:

@implementation QiShareMember { NSMutableSet *_friends; } - (instancetype)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSUInteger)age { if (self = [super init])  { _name = [name copy]; _sex = [sex copy]; _age = age; _friends = [NSMutableSet new]; } return self; } - (void)addFriend:(QiShareMember *)friend { [_friends addObject:friend]; } - (void)removeFriend:(QiShareMember *)friend { [_friends removeObject:friend]; } - (id)copyWithZone:(NSZone *)zone { QiShareMember *copy = [[[self class] allocWithZone:zone] initWithName:_name andSex:_sex andAge:_age]; copy->_friends = [_friends mutableCopy]; / /! < p style = "max-width: 100%; clear: both; min-height: 1em; } @endCopy the code

Finally, a special thanks to Effective Objective-C 2.0 chapter 3