Following on from the last article, iOS Interview Basics (part 1), let’s share the following iOS interview questions.

A, category

Unlike high-level languages such as C++ that can directly inherit from multiple classes, OC can use classes and protocols to implement multiple inheritance.

1. Category loading time

When the App loads, Runtime adds the class’s instance methods, protocols, and properties to the class; Add the class method of the Category to the metaclass of the class.

2. Add attributes and methods to categories

1) The @property method cannot be used to define properties in a class. OC does not generate setter and getter methods for class properties. You do that with objc_setAssociatedObject.

@interface TestClass(ak)

@property(nonatomic,copy) NSString *name;

@end

@implementation TestClass (ak)

- (void)setName:(NSString *)name{

    objc_setAssociatedObject(self,  "name", name, OBJC_ASSOCIATION_COPY);
}

- (NSString*)name{
    NSString *nameObject = objc_getAssociatedObject(self,  "name");
    return nameObject;
}

Copy the code

2) Class method override problem

  • If both the class and the main class have a method named funA, then after the class loads, the class’s method list will have two funAs;
  • Categories of methods are in the front of the new method on the list, and the method of the main class in the list at the back of the new method, which creates a class method “cover” method of the same name of the original class, this is because the runtime when search method is down the list of methods of sequential search, it as long as one to find corresponding name, would be unable to find, Little did they know that there might be another method with the same name;
  • If multiple categories define the method funA with the same name, the implementation of which category is called depends on the compilation order, and the implementation of the post-compiled category will be called.
  • In the daily development process, the same name of a class method causes incorrect invocation or crash. We can avoid the same name by prefixing the name of a class method.

For a more in-depth analysis of categories, see Meituan’s technical article for an in-depth understanding of Objective-C: Categories

Second, the agreement

define

Protocols in iOS are similar to interface classes in Java and C++. Protocols can be used to implement multiple inheritance and proxy in OC.

Method statement

Methods in the protocol can be declared @Required (implementations are required, but warnings are issued if they are not implemented, but compilation does not produce errors) or @optional (implementations are not required and there are no warnings if they are not implemented). I often ask candidates two questions: – How can you tell if a class implements a protocol? Many people do not know that conformsToProtocol can be used to judge. – If you ask a business to implement a delegate, how can you tell if the business is implementing a method of dalegate? A lot of people don’t know that you can do that by responding to Selector.

3. Notification Center

The notification center in iOS is actually an implementation of the Observer pattern.

Is postNotification a synchronous or asynchronous call?

Synchronous invocation. When you call the addObserver method to listen for notifications, and then call postNotification to throw notifications, postNotification will iterate through all the observers in the current thread, and then call the observer’s listening methods in turn. The code after the postNotification is executed only after the call is complete.

How to implement asynchronous listening notifications?

Through addObserverForName: object: queue: usingBlock to implement asynchronous notification.

Fourth, the KVC

KVC lookup order

1) when calling setValue:forKey, such as [obj setValue:@”akon” forKey:@”key”], it searches for members in the order of _key, _iskey, key, iskey, and then assigns values to them. If none is found, the setValue:forUndefinedKey method on the object is called, which by default throws an exception. 2) when calling the code for valueForKey:@”key”, KVC searches forKey differently than setValue”akon” forKey:@”key”. The search method is as follows:

  • First look for the getter method in get, is order, and if you find it, you call it directly. If it’s a BOOL or an Int equivalent, it wraps it up as an NSNumber object.
  • If not, KVC looks for a method in countOf, objectInAtIndex, or AtIndexes format. If the countOf method and one of the other two methods are found, then it returns a collection of proxies that respond to all of the methods of NSArray (it’s NSKeyValueArray, which is a subclass of NSArray), calls the methods of this collection of proxies, It is called as a combination of countOf,objectInAtIndex, or AtIndexes. There is also an optional get:range: method. So if you want to redefine some of the functionality of KVC, you can add these methods, but be careful that your method names conform to KVC’s standard named methods, including method signatures. – If none of the above methods are found, we will look for countOf, enumeratorOf, and memberOf methods. If all three methods are found, then it returns a collection of proxies that respond to all the methods of the NSSet. As above, sending the NSSet message to the collection of proxies is called as a combination of countOf, enumeratorOf, and memberOf.
  • If you haven’t found, check class method + (BOOL) accessInstanceVariablesDirectly, if return YES (default behavior), then the set value and the previous, will follow the _, _is,, is the order of the search member variable names.
  • If not, call the object’s valueForUndefinedKey: method directly, which by default throws an exception.

KVC crash-proof

We often use KVC to set and get properties, but if an object does not declare properties according to KVC rules, it will crash. How to prevent such crashes globally and generally? You can prevent crashes by writing an NSObject class.

@interface NSObject(AKPreventKVCCrash)

@end

@ implementation NSObject(AKPreventKVCCrash)

- (void)setValue:(id)value forUndefinedKey:(NSString *)key{    
}

- (id)valueForUndefinedKey:(NSString *)key{

    return nil;
}

@end

Copy the code

Five, the KVO

define

Key-value Observing (KVO) : indicates key-value Observing. It is a derivative of the observer pattern. The basic idea is to add an observation to a property of the target object, and automatically notify the observer when the property changes by triggering the KVO interface method implemented by the observer object.

Register and remove KVO

There are two ways to register and remove KVO

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

Copy the code

Use the observeValueForKeyPath to get changes in the value.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context

Copy the code

KVO can be easily done through the Facebook open source library KVOController.

KVO implementation

The KVO implementation is described in apple’s official documentation as follows:

Key-Value Observing Implementation Details Automatic key-value observing is implemented using a technique called isa-swizzling. The isa pointer, as the name suggests, points to the object’s class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data. When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance. You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.

When an object of type ObjectA is added, the system generates a derived class, NSKVONotifying_ObjectA, and points the object’s ISA pointer to the new class. This means that the object’s type changes. So when you send a message to an ObjectA object, you’re actually sending it to a method of a derived object. Because the compiler override the method of the derived class and adds the notification code, a notification is sent to the registered object. Note that the derived class overrides only the attribute methods that register observers.

For more detailed information about KVC and KVO, refer to iOS KVC and KVO details

Six, autorelasepool

use

With ARC, we don’t need to manage memory manually, we can manage memory correctly without even knowing that autoRelease exists, because Runloop automatically creates and frees autorelease pools within each Runloop Circle. Manually created AutoReleasepool is a good way to avoid memory spikes when a large number of objects need to be created and destroyed. If not manually created, the outer pool will drain after the entire Runloop Circle is completed. If manually created, the drain operation will drain after the block is completed. For example:

for (int i = 0; i < 100000; i++) { @autoreleasepool { NSString* string = @"akon"; NSArray* array = [string componentsSeparatedByString:string]; }}Copy the code

For example, this code in SDWebImage, because the encoding dataWithImage will decode the image into data, may cause memory inflation, so add autoreleasepool to avoid memory inflation

@autoreleasepool { NSData *data = imageData; if (! data && image) { // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format SDImageFormat format; if ([SDImageCoderHelper CGImageContainsAlpha:image.CGImage]) { format = SDImageFormatPNG; } else { format = SDImageFormatJPEG; } data = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil]; } [self _storeImageDataToDisk:data forKey:key]; }Copy the code

Automatically release pool creation and release timing in Runloop

  • The autoReleaspool created by the system in Runloop is released at the end of an event in Runloop.
  • The autoReleasepool we created manually will drain after the block completes. Note that: When a block ends with an exception, the pool will not be drained. The drain operation of the pool subtracted the reference count of all objects marked autoRelease by one, but this does not mean that the object will necessarily be released. We can manually retain the object in the AutoRelease pool to extend its lifetime (in the MRC).

Visit myGithubWarehouse view more wonderful