objc.h , runtime.h and message.h . First, look at the objc.h file and see what the object is from the point of view of Runtime: typedef struct objc_class *Class; typedef struct objc_object { Class isa; } *id;  struct objc_class { Class isa; };  typedef struct objc_selector *SEL; typedef struct objc_method *Method; typedef struct objc_ivar *Ivar; typedef struct objc_category *Category; typedef struct objc_property *objc_property_t; class_addMethod , class_addIvar , class_replaceMethodclass_allocateClassPair , class_registerClassPairclass_getName , class_getSuperclass , class_getInstanceVariable , class_getProperty , class_copyMethodList , class_copyIvarList , class_copyPropertyListobjc_msgSend , objc_getClass , object_copy @interface COConcreteObject : COBaseObject @property(nonatomic, strong) NSString *name; @property(nonatomic, strong) NSString *title; @property(nonatomic, strong) NSNumber *quantity; @end <COConcreteObject: 0x71d6860> . Since the data model is quite extensive, with a large number of different subclasses, it is undesirable to write for each class a separate description method in which to manually collect the values ​​of its properties. Objective-C Runtime comes to the rescue: @implementation COBaseObject - (NSString *)description { NSMutableDictionary *propertyValues = [NSMutableDictionary dictionary]; unsigned int propertyCount; objc_property_t *properties = class_copyPropertyList([self class], &propertyCount); for (unsigned int i = 0; i < propertyCount; i++) { char const *propertyName = property_getName(properties[i]); const char *attr = property_getAttributes(properties[i]); if (attr[1] == '@') { NSString *selector = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]; SEL sel = sel_registerName([selector UTF8String]); NSObject * propertyValue = objc_msgSend(self, sel); propertyValues[selector] = propertyValue.description; } } free(properties); return [NSString stringWithFormat:@"%@: %@", self.class, propertyValues]; } @end class_copyPropertyList function. Then property values ​​are collected in NSDictionary , which is used when constructing the string representation of the object. This algorithm works only with properties that are Objective-C objects. Type checking is performed using the property_getAttributes function. The result of the method looks like this:objc_msgSend : //   [array insertObject:foo atIndex:1]; //    Runtime- objc_msgSend(array, @selector(insertObject:atIndex:), foo, 1); objc_msgSent initiates the process of searching for the implementation of the method corresponding to the selector passed to the function. The implementation of the method is looked up in the so-called class dispatch table. Since this process can be quite long, a cache of methods is associated with each class. After the first call of any method, the result of the search for its implementation will be cached in the class. If the implementation of the method is not found in the class itself, then the search continues up the inheritance hierarchy in the superclasses of this class. If, however, the result is not reached in the hierarchy search, the dynamic search mechanism comes into resolveInstanceMethod - one of the special methods is called: resolveInstanceMethod or resolveClassMethod . Redefining these methods is one of the last ways to influence Runtime: + (BOOL)resolveInstanceMethod:(SEL)aSelector { if (aSelector == @selector(myDynamicMethod)) { class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:"); return YES; } return [super resolveInstanceMethod:aSelector]; } NSMutableArray array. Standard language tools do this not work. But we can use a technique called method swizzling: @implementation NSMutableArray (CO) + (void)load { Method addObject = class_getInstanceMethod(self, @selector(addObject:)); Method logAddObject = class_getInstanceMethod(self, @selector(logAddObject:)); method_exchangeImplementations(addObject, logAddObject); } - (void)logAddObject:(id)aObject { [self logAddObject:aObject]; NSLog(@"  %@   %@", aObject, self); } @end load method — this is a special callback that, if defined in a class, will be called during the initialization of this class — before any of its other methods are called. Here we swap the implementation of the base method addObject: and our method logAddObject: Note the “recursive” call to logAddObject: - this is the call to the overloaded implementation of the main method.UITableView - a link to the “stub”, which will be shown when the table is empty: @interface UITableView (Additions) @property(nonatomic, strong) UIView *placeholderView; @end  static char key; @implementation UITableView (Additions) -(void)setPlaceholderView:(UIView *)placeholderView { objc_setAssociatedObject(self, &key, placeholderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(UIView *) placeholderView { return objc_getAssociatedObject(self, &key); } @end objc_setAssociatedObject function. Its operation requires a key by which you can then retrieve the object you need back using the objc_getAssociatedObject call. In this case, you cannot use the copied key value - it must be exactly the object (in the example, a pointer) that was passed in the objc_setAssociatedObject call.Source: https://habr.com/ru/post/177421/
All Articles