Introduction:
If you read this book, you may have mastered the basics of iOS development. However, there are some minor features and practices that many developers are not familiar with, even for developers who have years of experience. In this chapter, you will learn some important development skills, but this is far from enough. You still need to accumulate more practices to make your code more powerful.
/*
This article is translated from Chapter 3 "You May Not Know" in iOS 7 Programming Pushing the Limits. If You want to understand the essence of the original article, please support the original book.
------ (Blog Park, Sina Weibo)
*/
Directory:
I. Best naming practices
Ii. Best practices for Property and instance variables (Ivar)
Iii. Category)
4. Associative References)
5. Weak Collections
Vi. NSCache
VII. NSURLComponents
8. CFStringTransform
IX. instancetype
10. Base64 and Percent Encoding
11.-[NSArray firstObject]
12. Summary
13. Read more
I. Best naming practices
In iOS development, naming conventions are extremely important. In the following section, we will learn how to name various entries correctly and why.
1. Automatic Variables
Cocoa is a dynamic language, and you are easily confused about the type used. Collections (arrays, dictionaries, and so on) are not associated with their types, so such exceptions can easily occur:
1 NSArray *dates = @[@”1/1/2000”];2 NSDate *firstDate = [dates firstObject];
The compiler has no warning, but when you use firstDate, it is likely to report an unknown selector exception ). The error is that a string dates array is called. This array should call dateStrings or contain the NSDate object. So careful naming will avoid many headaches.
2. Method
1) The method name should clearly indicate the type of receipt and return
For example, the method name is confusing:
1-(void) add; // confusing
It seems that add should contain some parameters, but it does not. Does it add some default objects?
The name is much clearer:
1 - (void)addEmptyRecord;2 - (void)addRecord:(Record *)record;
Now addRecord: receives a Record parameter, which looks much clearer.
2) The object type should match the name. If the type and name do not match, it is easy to confuse
This example shows a common error:
1-(void) setURL :( NSString *) URL; // incorrect
This error occurs because an NSURL instead of an NSString should be received when setURL is called. If you need a string, you need to add some instructions to make it clearer:
1 - (void)setURLString:(NSString *)string;2 - (void)setURL:(NSURL *)URL;
This rule should not be excessively used. If the type is obvious, do not add the type information to the variable. A property named name is better than a property named nameString.
3) method names also have specific principles related to memory management and KVC
Although ARC makes some of these rules no longer important, when ARC interacts with non-ARC (including non-ARC Code of the Apple framework ), incorrect naming rules will still cause very challenging errors.
The method name should always start with a lower-case letter and have a hump structure.
If a method name starts with alloc, new, copy, or nutableCopy, the caller has the returned object. If the name of your property is like newRecord, this rule may cause problems. Please change the name.
A reference value should be returned at the beginning of the get method, for example:
1 - (void)getPerson:(Person **)person;
Do not use the get prefix as part of the property accessor. The getter of the property name should be-name.
Ii. Best practices for Property and instance variables (Ivar)
Property should represent the state of an object, and Getter should have no external impact (they can have internal effects, such as caching, but those should be invisible to callers ).
To avoid direct access to instance variables, Use accessor instead.
In early ARC, the most common cause of bugs was direct access to instance variables. If developers do not have the correct retain and release instance variables, their applications will crash or memory leakage. Because ARC automatically manages retain and release, some developers think that this rule is no longer important, but there are other reasons for using accessors:
- KVO
- Perhaps the most critical reason for using accessor is that property can be observed. If you do not use accessor, you need to call willChangeValueForKey: And didChangeValueForKey: each time you modify the instance variables in the property. accessor will automatically call these methods as needed.
- Side effects
- In setter, you or your subclass may contain side effects. Notifications may be sent and events may be registered in NSUndoManager. You should not bypass these side effects unless they are necessary.
- Lazy instantiation
- If a property is lazily instantiated, you must use accessor to ensure its correct initialization.
- Locking
- If locking is introduced to a property to manage multi-threaded code, direct access to instance variables will violate your lock and may cause program crash.
- Consistency
- After seeing the preceding content, someone may say that only accessor should be used, but this makes the code difficult to maintain. Suspicion and explanation of every directly accessed instance variable, rather than remembering which require accessor and which do not, makes the code easier to review, review, and maintain. Accessor, especially synthesized accessors, has been highly optimized in OC and they are worth using.
That is to say, you should not use accessor in these places:
- Inside Accessor
- Obviously, you cannot use your own accessor. Generally, you do not want to use getter or setter itself (which may create an infinite loop). An accessor should access its own instance variables.
- Dealloc
- ARC greatly reduces dealloc, but sometimes it still appears. It is best to call an external object in dealloc, which may be in an inconsistent state and may cause confusion.
- Initialization
- Similar to dealloc, objects may be inconsistent during initialization. You should not destroy notifications or other side effects at this time.
Iii. Category)
Classification allows you to add methods in the running class. Any class (or even the Cocoa class provided by Apple) can be expanded through classification. These new methods are available to all instances of the class. The classification declaration is as follows:
1 @interface NSMutableString (PTLCapitalize)2 - (void)ptl_capitalize;3 @end
PTLCapitalize is the name of the category. Note that no instance variables are declared here.
Neither instance variables nor synthesize properties can be declared for a category.
You can declare properties for classification because it is only another way to declare methods.
CATEGORY cannotSynthesizeProperties, because this creates an instance variable.
1. + load
Classes are appended to classes at runtime, which may be defined as dynamic loading, so classes can be added very late (although you cannot write your own dynamic library in iOS, but the system framework is dynamically loaded and includes classification ). OC provides an object named + load that runs when the category is appended for the first time. With + initialize, you can use it to set the specified category, such as initializing static variables. You cannot safely use + initialize in the classification, because the class may have implemented it. If there are multiple classification implementations + initialize, it makes no sense to run one.
I hope you are ready to ask an obvious question: "If the classification cannot use + initialize, because they may conflict with other categories, what about multiple classification implementations + load ?" This is one of the magic points of OC runtime. The + load method is a special case of runtime. It can be implemented by every classification and All implementations are run. Of course, you should not try to call + load manually.
4. Associative References)
Associated references allow you to attach key-value data to any object. This capability can be used for multiple purposes, but the most common feature is to allow you to add data properties to a category.
Consider a Person class. You want to use the class to add a new property called emailAddress. Maybe you use the Person class in other programs and sometimes use email address instead of email address. Therefore, using classification is a good solution to avoid overhead. Or, you don't have your own Person class and the maintainer won't add the property for you. How do you solve this problem? First, let's take a look at the basic Person class:
1 @interface Person : NSObject2 @property (nonatomic, readwrite, copy) NSString *name;3 @end4 5 @implementation Person6 @end
Now you can add a new property and use the association reference in the category:
1 #import <objc/runtime.h> 2 @interface Person (EmailAddress) 3 @property (nonatomic, readwrite, copy) NSString *emailAddress; 4 @end 5 6 @implementation Person (EmailAddress) 7 static char emailAddressKey; 8 9 - (NSString *)emailAddress {10 return objc_getAssociatedObject(self, &emailAddressKey);11 }12 13 - (void)setEmailAddress:(NSString *)emailAddress {14 objc_setAssociatedObject(self, &emailAddressKey, emailAddress, OBJC_ASSOCIATION_COPY);16 } 17 @end
Note that the associated reference is a key-based memory address, not its value. What is stored in emailAddressKey is not important. It only needs a unique and unchanged address, which is why it usually uses an unallocated static char as the key.
Association references have good memory management. They are used to pass correct copy, assign, or retain with reference to the parameter passing of objc_setAssociatedObject. When the related objects are deallocated, they will be released. This actually means that when another object is destroyed, you can use the relevant object for tracking, for example:
1 const char kWatcherKey; 2 3 @interface Watcher : NSObject 4 @end 5 6 #import <objc/runtime.h> 7 8 @implementation Watcher 9 - (void)dealloc {10 NSLog(@"HEY! The thing I was watching is going away!");11 }12 @end13 ...14 NSObject *something = [NSObject new];
15 objc_setAssociatedObject(something, &kWatcherKey, [Watcher new], OBJC_ASSOCIATION_RETAIN);
This technology is useful for debugging and can also be used for non-debugging tasks, such as cleaning.
Using correlated references is a good way to attach related objects to alert panel or control. For example, you can append a "represented object" to alert panel. The Code is as follows:
1 ViewController.m (AssocRef) 2 id interestingObject = ...; 3 UIAlertView *alert = [[UIAlertView alloc] 4 initWithTitle:@"Alert" message:nil 5 delegate:self 6 cancelButtonTitle:@"OK" 7 otherButtonTitles:nil]; 8 objc_setAssociatedObject(alert, &kRepresentedObject, 9 interestingObject,10 [alert show];
Many programs use instance variables to process this task, but the associated references are more concise. For developers familiar with Mac, the code is similar to representedObject.
But it is more flexible.
Lenovo references a restriction that they are not integrated with encodeWithCoder: So they are difficult to serialize through a classification.
V. Weak Collections
Most Cocoa collections such as NSArray, NSSet, and NSDictionary have powerful functions, but they are not suitable for certain situations. NSArray and NSSet will keep the objects you store, And NSDictionary will save values and keys. These actions are usually what you want, but they are not suitable for some work. Fortunately, some other collections have appeared since iOS6: NSPointerArray, NSHashTable, and NSMapTable. They are collectively referred to as the pointer collection classes in the Apple documentation, and sometimes use the NSPointerFunctions class for configuration.
NSPointerArray is similar to NSArray, and NSHashTable is similar to NSSet, while NSMapTable is similar to NSDictionary. Each new collection class can be configured to maintain weak references, pointing to null objects or other exceptions. An additional benefit of NSPointerArray is that it can also store NULL values.
The pointer collection class can use NSPointerFunctions for extensive configuration, but in most cases, it simply transfers a NSPointerFunctionsOptions flag to-initWithOptions :. The most common situation is that + weakObjectsPointerArray has its own constructor.
Vi. NSCache
NSCache has several underestimated functions. For example, in fact, it is thread-safe, and you may change a NSCache in any lockless thread. NSCache is also designed to integrate object compliance with <NSDiscardableContent>. The most common type is NSPurgeableData. By calling beginContentAccess and endContentAccess, you can control when to safely discard this object. This not only provides automatic Cache Management when your application is running, but also helps your application to be suspended. In general, when the memory is insufficient, the memory warning does not release enough memory, and iOS will start to kill Applications suspended in the background. In this case, your application does not get the delegate information, so it is killed. However, if you use NSPurgeableData, iOS releases the memory, even if your application is paused.
Want to get