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_replaceMethod
class_allocateClassPair
, class_registerClassPair
class_getName
, class_getSuperclass
, class_getInstanceVariable
, class_getProperty
, class_copyMethodList
, class_copyIvarList
, class_copyPropertyList
objc_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