Good articles to my personal technology blog: https://cainluo.github.io/15034036545472.html


In the previous chapter, we took a look at some of the basic RunTime concepts and some of the little things that make up the RunTime.

For those of you who haven’t joined the pack, check out The Pack for iOS Development: Pack For iOS Development – RunTime.

Reprint statement: if you need to reprint this article, please contact the author, and indicate the source, and can not modify this article without authorization.


The use of objc_msgSend

In the previous article, we rewrote the runtimemodel. m file with Clang to get runtimemodel. CPP, which is mostly implemented in C code.

Objc_msgSend is the same project as before. Here we add a test class:

#import "TestModel.h"

@implementation TestModel

- (void)country {
    NSLog(@ "China");
}

- (void)getProvince:(NSString *)provinceName {
    NSLog(@ "% @", provinceName);
}

- (void)getCity:(NSString *)cityName
        station:(NSString *)stationName {
    
    NSLog(@ % @, % @ "", cityName, stationName);
}

- (NSString *)getWeather {
    
    return @ "sunny day";
}

@end
Copy the code

Call:

- (void)test {
    
    TestModel *objct = [[TestModel alloc] init];
    
    ((void (*) (id, SEL)) objc_msgSend) (objct, sel_registerName("country"));
    
    ((void (*) (id, SEL, NSString *)) objc_msgSend) (objct, sel_registerName("getProvince:"), @" Guangdong");
    
    ((void (*) (id, SEL, NSString *, NSString *)) objc_msgSend) (objct, sel_registerName("getCity:station:"), @" Shenzhen".@windows on the World);
    
    NSString *weather = ((NSString* (*) (id, SEL)) objc_msgSend) (objct, sel_registerName("getWeather"));
    
    NSLog(@ "% @", weather);
}
Copy the code

Printed results:

2017-08-22 20:52:00.497 1.RunTime[34290:2794192] China2017-08-22 20:52:00.497 1.RunTime[34290:2794192] in guangdong province2017-08-22 20:52:00.497 1.RunTime[34290:2794192Shenzhen, window to the world2017-08-22 20:52:00.498 1.RunTime[34290:2794192] a sunny dayCopy the code

I just declared the method in the testModel.m file, but I can still call it with objc_msgSend.

If we take a look at the code, we can also see that objc_msgSend does a strong cast. If we kill the strong cast, Xcode will report an error:

Too many arguments to function call, expected 0, have 4.
Copy the code

This error depends on the size of your method arguments.


objc_msgSendSuper

In addition to the objc_msgSend we just saw, there are many more, such as:

  • Objc_msgSend: Sends a message with a simple return value to an instance of a class.
  • Objc_msgSend_fpret: Sends a message with a floating-point return value to an instance of the class
  • Objc_msgSend_stret: Sends a message with a data structure return value to the instance of the class
  • Objc_msgSendSuper: A superclass that sends a simple return value message to an instance of the class
  • Objc_msgSendSuper_stret: A superclass that sends a message with a data structure return value to an instance of the class

Here we will focus on objc_msgSendSuper, which is defined in the #import

file as:

OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
Copy the code

Normally when we call Super, the Runtime will call objc_msgSendSuper, for example:

[super methodName];
Copy the code

We can override init in TestModel and print:

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        
        NSLog(@ "% @"[self class]);
        NSLog(@ "% @"[super class]);
    }
    
    return self;
}
Copy the code

With that done, we can use Clang to refactor:

PS: Remember which folder you are inClangRewrite, so the newly generated file is there.

Then you can find it in the testModel.cpp file:

NSLog((NSString *)&__NSConstantStringImpl__var_folders_86_ycmkjs0s48l_knc_xnscdqq00000gn_T_TestModel_ece3b7_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));

NSLog((NSString *)&__NSConstantStringImpl__var_folders_86_ycmkjs0s48l_knc_xnscdqq00000gn_T_TestModel_ece3b7_mi_1, ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("TestModel"))}, sel_registerName("class")));
Copy the code

So when we call [super methodName], the Runtime changes to objc_msgSendSuper, and it does this:

  • To constructobjc_superThe structure of the body
    • The first member variable isself.
    • The second is the(id) class_getSuperclass (objc_getClass (" TestModel ")).
  • And then you go to the superclass- (Class)classMethod, if not found, will continue to go up a layer to find, always findNSObjectWhen it is found, it will be used internallyobjc_msgSend(objc_super->receiver, @selector(class))I’m going to call it, and I’m going to call it[self class]Call the same, so the output is always zeroTestModel.

Object associated

Object association allows developers to add attributes to the class of an existing class’s Category:

OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) OBJC_AVAILABLE(10.6.3.1.9.0.1.0);
Copy the code
  • Object: indicates the source object
  • Key: is the associated key,
  • Value: indicates the associated object
  • Policy: is an enumeration

The policy:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0./**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1./**< Specifies a strong reference to the associated object. * The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3./**< Specifies that the associated object is copied. * The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401./**< Specifies a strong reference to the associated object. * The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied. * The association is made atomically. */
};
Copy the code

If we want to get an attribute, we can use the following method, also with the associated Key:

OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key) OBJC_AVAILABLE(10.6.3.1.9.0.1.0);
Copy the code

If you want to delete an associated object, just set objc_setAssociatedObject and set the object to nil:

objc_setAssociatedObject(self, AttributeKey, nil, OBJC_ASSOCIATION_COPY_NONATOMIC);
Copy the code

If we use objc_removeAssociatedObjects, we remove all associated objects:

OBJC_EXPORT void objc_removeAssociatedObjects(id object) OBJC_AVAILABLE(10.6.3.1.9.0.1.0);
Copy the code

Let’s get straight to the code:

#import "TestModel.h"

@interface TestModel (String)

@property (nonatomic.copy) NSString *testString;

@end
Copy the code
#import "TestModel+String.h"
#import <objc/runtime.h>

static void *TestStringKey = &TestStringKey;

@implementation TestModel (String)

- (void)setTestString:(NSString *)testString {
    
    objc_setAssociatedObject(self, TestStringKey, testString, OBJC_ASSOCIATION_COPY);
}

- (NSString *)testString {
    
    return objc_getAssociatedObject(self, TestStringKey);
}

@end
Copy the code

Then go back to the Controller and import the header file, calling:

- (void)test {
    
    TestModel *objct = [[TestModel alloc] init];
    
    ((void (*) (id, SEL)) objc_msgSend) (objct, sel_registerName("country"));
    
    ((void (*) (id, SEL, NSString *)) objc_msgSend) (objct, sel_registerName("getProvince:"), @" Guangdong");
    
    ((void (*) (id, SEL, NSString *, NSString *)) objc_msgSend) (objct, sel_registerName("getCity:station:"), @" Shenzhen".@windows on the World);
    
    NSString *weather = ((NSString* (*) (id, SEL)) objc_msgSend) (objct, sel_registerName("getWeather"));
    
    NSLog(@ "% @", weather);
    
    objct.testString = @ "Ming";
    
    NSLog(@"Category: %@", objct.testString);
}
Copy the code
2017-08-23 00:09:38.236 1.RunTime[35345:2926512] TestModel
2017-08-23 00:09:38.236 1.RunTime[35345:2926512] TestModel
2017-08-23 00:09:38.236 1.RunTime[35345:2926512] China2017-08-23 00:09:38.237 1.RunTime[35345:2926512] in guangdong province2017-08-23 00:09:38.237 1.RunTime[35345:2926512Shenzhen, window to the world2017-08-23 00:09:38.237 1.RunTime[35345:2926512] a sunny day2017-08-23 00:09:38.237 1.RunTime[35345:2926512Category: xiao MingCopy the code

The project address

The address of the project: https://github.com/CainRun/iOS-Project-Example/tree/master/RunTime/Two

Note:TestModel.cppIn the catalog, I didn’t put it in the project.


The last