This article belongs to “Jane Book — Liu Xiaozhuang” original, please note:

< Jane books – Liu Xiaozhuang > https://www.jianshu.com/p/4a22a39b69c5



attribute

__attribute__ is a set of compiler directives, supported by the GNU and LLVM compilers, that allow for adding parameters to __attribute__ and doing advanced checks and optimizations.

The syntax for __attribute__ is to write a list of attributes separated by commas, followed by two parentheses. In iOS, many macro definitions, such as NS_CLASS_AVAILABLE_IOS, are internally implemented via __attribute__.

__attribute__((attribute1, attribute2));
Copy the code

Here are some common attributes for __attribute__. A more complete list of attributes can be found on the LLVM website.

objc_subclassing_restricted

The objc_subclassing_restricted attribute indicates that the modified class cannot be inherited by other classes, otherwise the following error will be reported.

__attribute__((objc_subclassing_restricted)) @interface TestObject : NSObject @property (nonatomic, strong) NSObject *object; @property (nonatomic, assign) NSInteger age; @end @interface Child : TestObject @end Cannot subclass a class that was declared with the 'objc_subclassing_restricted' attributeCopy the code

objc_requires_super

The objC_REQUIres_super attribute indicates that a subclass must call the decorated method super, otherwise it will warn yellow.

@interface TestObject : NSObject
- (void)testMethod __attribute__((objc_requires_super));
@end

@interface Child : TestObject
@endWarning: Method Possibly missing a [super testMethod] call
Copy the code

constructor / destructor

The constructor attribute indicates that some action can be performed before the main function executes. The destructor property indicates that some action is done after the execution of the main function. Constructor is executed only after all load methods have been executed before any functions decorated by the constructor attribute are executed.

__attribute__((constructor)) static void beforeMain() {
    NSLog(@"before main");
}

__attribute__((destructor)) static void afterMain() {
    NSLog(@"after main");
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"execute main");
    }
    return 0; } Debug -objc[23391:1143291] before main
debug-objc[23391:1143291] execute main
debug-objc[23391:1143291] after main
Copy the code

When there are functions decorated with multiple constructor or destructor attributes, you can specify the order of execution by setting priority. The format is __attribute__((constructor(101))), followed directly by the priority.

__attribute__((constructor(103))) static void beforeMain3() {
    NSLog(@"after main 3");
}

__attribute__((constructor(101))) static void beforeMain1() {
    NSLog(@"after main 1");
}

__attribute__((constructor(102))) static void beforeMain2() {
    NSLog(@"after main 2");
}
Copy the code

The lower the priority in constructor, the higher the order of execution. Destructor, on the other hand, has a higher priority and higher order of execution.

overloadable

The Overloadable attribute allows you to define multiple functions with the same name but different parameter types, and the compiler automatically matches the function based on the type of argument passed in at call time. This is similar to C++ ‘s function overloading, but it happens at compile time.

__attribute__((overloadable)) void testMethod(int age) {}
__attribute__((overloadable)) void testMethod(NSString *name) {}
__attribute__((overloadable)) void testMethod(BOOL gender) {}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        testMethod(18);
        testMethod(@"lxz");
        testMethod(YES);
    }
    return 0;
}
Copy the code

objc_runtime_name

The objc_runtime_name attribute can specify a different name for Class or Protocol at compile time, and the new name is independent of the naming convention and can start with a number.

__attribute__((objc_runtime_name("TestObject")))
@interface Object : NSObject
@end

NSLog(@ "% @".NSStringFromClass([TestObject class])); Result: TestObjectCopy the code

This property can be used for code obfuscation, such as writing a macro definition that implements the obfuscation logic inside. For instance of the Object through the MD5 do confusion, 32-bit confusion as a result, 497031794414 a552435f90151ac3b54b, who can tell what is this class. If you are afraid to match the rainbow table, add salt logic.

cleanup

With the cleanup property, you can assign a variable to execute a function before the variable is released. The specified function is executed before dealloc. In the specified function, you can pass in a parameter called cleanup, which is an address.

static void releaseBefore(NSObject **object) {
    NSLog(@ "% @", *object);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        TestObject *object __attribute__((cleanup(releaseBefore))) = [[TestObject alloc] init];
    }
    return 0;
}
Copy the code

When multiple cleanup attributes are encountered in the same code block, they are called in the order they were added when the code block scope ends.

unused

Another useful attribute is a yellow warning for unused variables in a project. The object may sometimes be retrieved in other ways, so you don’t want the warning to appear. You can use the unused property to remove the warning.

NSObject *object __attribute__((unused)) = [[NSObject alloc] init];
Copy the code

The system definition

The __attribute__ keyword is also used extensively in the system, but the system does not use __attribute__ directly externally, instead defining it as a macro definition that appears externally.

// NSLog
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1.2) NS_NO_TAIL_CALL;
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))

// the method of the parent class must be called
#define NS_REQUIRES_SUPER __attribute__((objc_requires_super))

// Specify the initialization method, which must be invoked directly or indirectly
#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
Copy the code

ORM

Object Relational Mapping, or ORM for short, is used to transform data between different systems in object-oriented languages. JSON to model can be realized through object-relational mapping. Mantle, MJExtension, YYKit, JSONModel and other frameworks are widely used. These frameworks are implemented in the way of Runtime when converting.

Mantle usage is somewhat similar to MJExtension, but MJExtension is more convenient to use. Mantle is mainly processed by inheritance, while MJExtension is processed by Category. The code is less dependent and non-invasive.

The performance evaluation

Among these third parties, Mantle has the most powerful function, but it is too bloated and its performance is worse than other third parties. JSONModel, MJExtension these third parties are almost at a level, YYKit can be relatively comparable to the performance of handwritten assignment code, the highest cost performance.

For projects where model conversion requirements are not too large, YYKit will be used for conversion performance as much as possible. It’s probably a little less functional than MJExtension, which I’m used to.

YYKit author review

Implementation approach

You can also implement the logic of model transformation by yourself. Taking dictionary to model as an example, the general logic is as follows:

  1. To create aCategoryIt is used to do model transformation, providing methods and passing in dictionary objects.
  2. throughRuntimeThe corresponding function retrieves the property list and iterates, fetching the corresponding object from the dictionary based on the property name.
  3. throughKVCAssigns a value from the dictionary to an object.
  4. Sometimes you encounter multiple levels of nesting, such as dictionaries containing an array, and an array containing a dictionary. If the model object is an array, fetch the array of fields corresponding to the dictionary, then iterate through the array and call the dictionary assignment method.

The following is a simple dictionary-to-model code that iterates through the attribute list via Runtime, fetching objects from the dictionary based on the attribute name, and then performing assignment via KVC. The call method is similar to MJExtension and YYModel. It can directly call the class method through the model class. If you want to use it in other classes, you should put the following implementation in NSObject Category so that all classes can call it.

// Call part
NSDictionary *dict = @{@"name" : @"lxz".@"age" : @18.@"gender" : @YES};
TestObject *object = [TestObject objectWithDict:dict];

// Implementation code
@interface TestObject : NSObject
@property (nonatomic.copy  ) NSString *name;
@property (nonatomic.assign) NSInteger age;
@property (nonatomic.assign) BOOL gender;

+ (instancetype)objectWithDict:(NSDictionary *)dict;
@end

@implementation TestObject

+ (instancetype)objectWithDict:(NSDictionary *)dict {
    return [[TestObject alloc] initWithDict:dict];
}

- (instancetype)initWithDict:(NSDictionary *)dict {
    self = [super init];
    if (self) {
        unsigned int count = 0;
        objc_property_t *propertys = class_copyPropertyList([self class], &count);
        for (int i = 0; i < count; i++) {
            objc_property_t property = propertys[i];
            const char *name = property_getName(property);
            NSString *nameStr = [[NSString alloc] initWithUTF8String:name];
            id value = [dict objectForKey:nameStr];
            [self setValue:value forKey:nameStr];
        }
        free(propertys);
    }
    return self;
}

@end
Copy the code

The Runtime allows you to get the Method List, Property List, etc. of an object, which can be used not only for dictionary model conversion, but also for a lot of work. For example, you can also use the Runtime to implement automatic archiving and anti-archiving.

// 1. Get all attributes
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([NJPerson class], &count);
// Archive through all attributes
for (int i = 0; i < count; i++) {
    // Retrieve the corresponding attribute
    Ivar ivar = ivars[i];
    const char * name = ivar_getName(ivar);
    // Convert the corresponding attribute name to an OC string
    NSString *key = [[NSString alloc] initWithUTF8String:name];
    // Use KVC to get data based on the attribute name
    id value = [self valueForKeyPath:key];
    [encoder encodeObject:value forKey:key];
}
free(ivars);
Copy the code

I wrote a simple Category that automatically implements the NSCoding, NSCopying protocol. This is the open source address: EasyNSCoding

The Runtime interview questions

Question 1

What does the following code output?

@implementation Son : Father
- (id)init {
    self = [super init];
    if (self) {
        NSLog(@ "% @".NSStringFromClass([self class]));
        NSLog(@ "% @".NSStringFromClass([super class]));
    }
    return self;
}
@end
Copy the code

Answer: all output Son.

The first NSLog output Son is definitely unsaid.

In the second output, [super Class] is converted to the following code.

struct objc_super objcSuper = {
    self,
    class_getSuperclass([self class]),};id (*sendSuper)(struct objc_super*, SEL) = (void *)objc_msgSendSuper;
sendSuper(&objcSuper, @selector(class));
Copy the code

The call to super is converted to the call to objc_msgSendSuper and passed in a structure of type objc_super. The structure takes two arguments, the first being the object that receives the message and the second being the corresponding parent of [super Class].

struct objc_super {
    __unsafe_unretained _Nonnull id receiver;
    __unsafe_unretained _Nonnull Class super_class;
};
Copy the code

As you can see, even though [super Class] is called, the object receiving the message is self. Then we go to the Father class method and print the Son class for self.

Question 2

The result of the following code?

BOOL res1 = [(id) [NSObject class] isKindOfClass:[NSObject class]].BOOL res2 = [(id) [NSObject class] isMemberOfClass:[NSObject class]].BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]].BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]].Copy the code

Answer: All three are NO except the first one.

Before speculating, there are two things to understand. What is the difference between isKindOfClass and isMemberOfClass? IsKindOfClass :class, the class of the object calling the method, returns YES for classes passed in the chain of heirs. IsMemberOfClass :class, the class of the object calling the modified method, must be the class passed in, returns YES.

Let’s analyze the results from the Runtime source.

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
Copy the code

The isKindOfClass and isMemberOfClass of object methods are only exposed during development, but the NSObject class implicitly implements the class method version. Not only those two methods, but all the other object methods in NSObject have their own versions of class methods. Because in OC, classes and metaclasses are also objects. Because all four calls are made from class objects, they end up executing the version of the class method.

First, the Runtime object model is taken out for later analysis.

The first caller is an NSObject class object, and the isKindOfClass method is passed in as a class object. Because calling the class method of a class returns the class itself, it is still the class object itself.

And then it goes into the for loop, and it starts iterating through NSObject’s metaclass, so the first time NSObject meta class! = NSObject class, failed to match. The second loop sets TCLS to the Superclass’s NSObject class, and NSObject class == NSObject class.

NSObject matches because this class is special, and when you get the superclass the second time, the superclass of the NSObject metaclass is the class object of NSObject, so it matches. The other three matches will all fail, so you can analyze the other three for yourself.

Question 3

The following code will? Compile Error/Runtime Crash/NSLog… ?

@interface NSObject (Sark)
+ (void)foo;
@end

@implementation NSObject (Sark)
- (void)foo {
    NSLog(@"IMP: -[NSObject (Sark) foo]");
}
@end

// Test the code
[NSObject foo];
[[NSObject new] performSelector:@selector(foo)];
Copy the code

Answer: All output normal, compile and run with no problems.

The first call will find the method from the metaclass, but the method is not in the metaclass, so find the superclass of the metaclass. The method is defined in the Category of NSObject, and because NSObject has a special object model, the superclass of the metaclass is a class object, you find the method from the class object and call it.

Question 4

The following code will? Compile Error/Runtime Crash/NSLog… ?

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

@implementation Sark
- (void)speak {
    NSLog(@"my name's %@".self.name);
}
@end

// Test the code
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    id cls = [Sark class];
    void *obj = &cls;
    [(__bridge id)obj speak];
}
@end
Copy the code

Answer: Execute normally, do not cause Crash.

After executing [Sark class] to obtain the class object, and then through the obj pointer to obtain the first address of the class object, this constitutes the basic structure of the object, can be called normally.

The original source

Sunnyxx- ObjC Runtime Admission Test for Neurological Hospitals

5

Why is there no weak under MRC?

In the MRC environment, you can also call the Runtime source code for weak. Weak source code is defined in the Private Headers Private folder, which requires #import “objc-internal.h” file.

Using the ARC source code below as an example, we define an object of type TestObject with an weak pointer to the created object.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        TestObject *object = [[TestObject alloc] init];
        __weak TestObject *newObject = object;
    }
    return 0;
}
Copy the code

This code is passed by the compiler to the following code, and the two functions in this code are the weak implementation functions, which can also be called under MRC.

objc_initWeak(&newObject, object);
objc_destroyWeak(&newObject);
Copy the code

Question 6

The same class, creating different objects, how to implement a specified object in dealloc to print a text?

The simplest way to solve this problem is to define a tag attribute in the class’s.h file, and print text in dealloc if the attribute is assigned a value of YES. However, this implementation is obviously not what the interviewer wants, will be directly passed ~

You can refer to the implementation of KVO, dynamically create a class at run time, this class is a subclass of the object, the newly created class dealloc implementation points to a custom IMP, and print a paragraph of text in the IMP. Set the object’s ISA to the newly created class, and the new class that ISA points to is executed when the dealloc method is executed.

thinking

A small problem

What calls technology big bull, how expresses technology strong?

I read a sentence some time ago, which I think can explain the above problem: “I can do all the functions of the app on the market.” This sentence is not comprehensive, should not just do, but better to do. This is good to evaluate from many aspects, performance, maintainability, completion time, product effect, etc., if these are done well, it is enough to prove that the person is very strong technology.

What’s the use of Runtime?

Runtime is a bit low-level, but what’s the point of going so deep? Is there any practical significance?

Runtime is of course used for practical purposes, not to mention that the entire OC is implemented through Runtime. For example, when we need to implement message forwarding, we need to use Runtime, or interception Method, and we also need to use Method Swizzling. Besides these, there are more usages to be explored.

Not only use it, but most importantly, understand the design of a language through the Runtime. It’s not just the various function calls in the Runtime. You can see what the OBJECT model of OC looks like as a whole.


Due to typesetting problems, the reading experience is not good, such as layout, picture display, code and many other problems. So go to Github and download the Runtime PDF collection. Put all the Runtime articles together in this PDF, with a table of contents on the left for easy reading.

Please give me a thumbs up, thank you! 😁