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 inClang
Rewrite, 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 construct
objc_super
The structure of the body- The first member variable is
self
. - The second is the
(id) class_getSuperclass (objc_getClass (" TestModel "))
.
- The first member variable is
- And then you go to the superclass
- (Class)class
Method, if not found, will continue to go up a layer to find, always findNSObject
When 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.cpp
In the catalog, I didn’t put it in the project.