Runtime: подробности и применение
1. Механизм работы во время выполнения
2. Применение
3. Основные функции и заголовочный файл
#import<objc/runtime.h> : //переменные-члены, класс, методы
class_copyIvarList : получение всех внутренних переменных-членов определённого класса
class_copyMethodList : получение всех методов определённого класса
class_getInstanceMethod : получение конкретного метода экземпляра (методы объекта, начинаются с -)
class_getClassMethod : получение определённого метода класса (начинаются с +)
method_exchangeImplementations : замена реализации двух методов
#import<objc/message.h> : //механизм сообщений
objc_msgSend(...)
4. Применение
#####1. Изменение значения свойства
#import <Foundation/Foundation.h>
@interface ZMPerson : NSObject
/** Имя **/
@property (nonatomic, copy) NSString *name;
/** Пол **/
@property (nonatomic, copy) NSString *sex;
- (NSString *)coding;
- (NSString *)eating;
- (NSString *)changeMethod;
@end
#import "ZMPerson.h"
#import <objc/runtime.h>
@interface ZMPerson(){
NSString *mobile;//номер телефона
}
/** Возраст **/
@property (nonatomic, copy) NSString *age;
@end
@implementation ZMPerson{
NSString *address;//адрес
}
- (NSString *)coding {
return @"coding";
}
- (NSString *)eating {
return @"eating";
}
- (NSString *)changeMethod {
return @"Метод был перехвачен и заменён";
}
// void(*)()
// У всех методов по умолчанию есть два скрытых параметра,
void eat(id self, SEL _cmd, NSNumber *meter){
NSLog(@"%@ __ %@ __ %@",[self class],NSStringFromSelector(_cmd),meter);
}
// Когда объект вызывает метод, который не реализован, вызывается этот метод для обработки, и список соответствующих методов передаётся.
// Это как раз можно использовать для определения того, является ли метод, который мы хотим динамически добавить, тем методом, который нам нужен.
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selStr = @"eat:";
SEL selFromStr = NSSelectorFromString(selStr);
if (sel == selFromStr) {
// Динамическое добавление метода eat
// Первый параметр: какому классу добавить метод
// Второй параметр: номер метода добавления
// Третий параметр: реализация функции (адрес функции)
// Четвёртый параметр: тип функции, (возвращаемое значение + тип параметра) v:void @:объект->self :означает SEL->_cmd
class_addMethod(self, sel, (IMP)eat, "v@:@");
}
return [super resolveInstanceMethod:sel];
}
@end
``` **Текст на русском языке:**
method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
}
return YES;
}
return NO;
}
@end
#import "NSObject+Swizzle.h"
@implementation MyString
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = object_getClass((id)self);
[class swizzleMethod:@selector(resolveInstanceMethod:) withMethod:@selector(myResolveInstanceMethod:)];
});
}
+ (BOOL)myResolveInstanceMethod:(SEL)sel {
if(! [self myResolveInstanceMethod:sel]) {
// NSString *selString = NSStringFromSelector(sel);
// class_addMethod(self, sel, class_getMethodImplementation(self, @selector(dynamicMethodIMP)), "v@:");
// return YES;
NSString *selString = NSStringFromSelector(sel);
///锁定一些方法如果不存在则创建
if([selString isEqualToString:@"countAll"] || [selString isEqualToString:@"pushViewController"] || [selString isEqualToString:@"test"]) {
class_addMethod(self, sel, class_getMethodImplementation(self, @selector(dynamicMethodIMP)), "v@:");
return YES;
}else {
return NO;
}
}
return YES;
}
- (void)dynamicMethodIMP {
NSLog(@"我是动态加入的函数");
}
В этом тексте описывается использование методов и классов для динамического изменения функциональности объектов в среде разработки iOS.
- (void)buttonClick:(UIButton *)sender {
NSLog(@"begin test");
MyString *string = [[MyString alloc] init];
[string performSelector:@selector(countAll)];
[string performSelector:@selector(pushViewController)];
[string performSelector:@selector(test)];
NSLog(@"finish test");
}
Этот фрагмент кода демонстрирует использование объекта MyString для вызова методов countAll, pushViewController и test.
@interface ZMCountButton : UIButton
@property (nonatomic, assign) NSInteger count;
@end
@interface ZMClickCount : NSObject
+ (instancetype)sharedInstance;
- (NSInteger)clickCount;
@end
Здесь описываются интерфейсы классов ZMCountButton и ZMClickCount, которые используются для реализации функционала подсчёта нажатий на кнопку.
#import <objc/runtime.h>
@implementation ZMCountButton
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method oriMethod = class_getInstanceMethod(self.class, @selector(sendAction:to:forEvent:));
Method cusMethod = class_getInstanceMethod(self.class, @selector(customSendAction:to:forEvent:));
// 判断自定义的方法是否实现, 避免崩溃
BOOL addSuccess = class_addMethod(self.class, @selector(sendAction:to:forEvent:), method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
if (addSuccess) {
// 没有实现, 将源方法的实现替换到交换方法的实现
class_replaceMethod(self.class, @selector(customSendAction:to:forEvent:), method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
} else {
// 已经实现, 直接交换方法
method_exchangeImplementations(oriMethod, cusMethod);
}
});
}
- (void)customSendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
_count = [[ZMClickCount sharedInstance] clickCount];
[self customSendAction:action to:target forEvent:event];
}
@end
@interface ZMClickCount()
@property (nonatomic, assign) NSInteger count;
@end
@implementation ZMClickCount
+ (instancetype)sharedInstance {
static ZMClickCount *_instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (NSInteger)clickCount {
++_count;
NSLog(@"点击了 %ld次", _count);
return _count;
}
@end
Эти фрагменты кода описывают реализацию функционала подсчёта количества нажатий на кнопки с использованием класса ZMCountButton.
- (void)countButtonClick:(UIButton *)sender {
self.testLabelText = [NSString stringWithFormat:@"点击 %ld 次了", self.testButton.count];
}
#pragma mark - 懒加载
- (ZMCountButton *)testButton {
if (!_testButton) {
_testButton = [ZMCountButton buttonWithType:UIButtonTypeCustom];
[_testButton addTarget:self action:@selector(countButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[_testButton setTitle:@"统计此按钮的点击数量" forState:UIControlStateNormal];
_testButton.frame = CGRectMake(20, self.view.center.y + 100, [UIScreen mainScreen].bounds.size.width - 40, 30);
_testButton.backgroundColor = [UIColor redColor];
}
return _testButton;
}
Данный фрагмент кода описывает использование класса ZMCountButton для создания кнопки, которая будет подсчитывать количество нажатий. ModelDelegate
@optional // Используется при преобразовании трёхмерного массива
@interface NSObject (NNKeyValues)
/** Преобразование словаря в модель **/
```oc
#import "NSObject+ZMKeyValues.h"
#import <objc/runtime.h>
@implementation NSObject (ZMKeyValues)
/** Преобразование словаря в модель **/
+ (instancetype)modelWithDict:(NSDictionary *)dict {
id objc = [[self alloc] init];
unsigned int count = 0;
// Получение списка переменных экземпляра класса
Ivar *ivarList = class_copyIvarList(self, &count);
// Перебор всех имён переменных экземпляра
for (int i = 0; i < count; i ++) {
// Получение переменной экземпляра
Ivar ivar = ivarList[i];
// Получение имени переменной экземпляра
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *key = [ivarName substringFromIndex:1];
// Извлечение соответствующего значения из словаря и присвоение его свойству модели
id value = dict[key];
// Получение типа переменной экземпляра
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// Проверка, является ли значение словарем
if ([value isKindOfClass:[NSDictionary class]]) {
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
Class modalClass = NSClassFromString(ivarType);
// Преобразование словаря в модель
if (modalClass) {
// Преобразование словаря в модель
value = [modalClass modelWithDict:value];
}
}
if ([value isKindOfClass:[NSArray class]]) {
// Определение, реализует ли класс соответствующий протокол для преобразования массива словарей в массив моделей
if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
// Приведение к типу id для возможности вызова методов любого объекта
id idSelf = self;
// Получение модели, соответствующей словарю в массиве
NSString *type = [idSelf arrayContainModelClass][key];
// Создание класса модели
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
// Итерация по массиву словарей и создание массива моделей
for (NSDictionary *dict in value) {
// Преобразование словаря в модель
id model = [classModel modelWithDict:dict];
[arrM addObject:model];
}
// Назначение массива моделей значению
value = arrM;
}
}
// KVC преобразование словаря в модель
if (value) {
[objc setValue:value forKey:key];
}
}
return objc;
}
@end
- (void)initView {
[super initView];
NSDictionary *person = @{@"name":@"Chen",
@"sex": @"男"};
NSDictionary *dict = @{@"coderID":@"99",
@"nickName": @"rattanchen",
@"phoneNumber": @"138****8888",
@"person" : person};
NSArray *addarr = @[dict ,dict, dict];
NSMutableDictionary *mudict = [NSMutableDictionary dictionaryWithDictionary:dict];
[mudict setObject:person forKey:@"person"];
for (NSDictionary *item in addarr) {
ZMCoding *coding = [ZMCoding modelWithDict:item];
[self.dataArray addObject:coding];
}
if (self.dataArray.count) {
self.testLabelText = @"Преобразование словаря в модель успешно, нажмите для просмотра соответствующих значений";
}
}
- (void)buttonClick:(UIButton *)sender {
ZMCoding *coding = self.dataArray.firstObject;
switch (sender.tag) {
case 0:
self.testLabelText = coding.coderID;
break;
case 1:
self.testLabelText = coding.nickName;
break;
case 2:
self.testLabelText = coding.phoneNumber;
break;
case 3:
self.testLabelText = coding.person.name;
break;
case 4:
self.testLabelText = coding.person.sex;
break;
default:
break;
}
}
- (NSArray *)buttonTitleArray {
return @[@"ID", @"псевдоним", @"номер телефона", @"имя", @"пол"];
}
- (NSMutableArray *)dataArray {
if (!_dataArray) {
_dataArray = [NSMutableArray array];
}
return _dataArray;
}
@end
``` ```
@interface ZMCoding: NSObject<NSCoding> {
ZMPerson *person;
}
@property (nonatomic, strong) ZMPerson *person;
@property (nonatomic, copy) NSString *coderID;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, copy) NSString *phoneNumber;
#import <objc/runtime.h>
@implementation ZMCoding {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
// 获取类中所有属性
for (int i = 0; i < count; i ++) {
// 取出 i 位置对应的属性
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivars);
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
}
return self;
}
- (void)initView {
[super initView];
ZMCoding *coding = [ZMCoding new];
coding.coderID = @"99";
coding.nickName = @"rattanchen";
coding.phoneNumber = @"138****8888";
NSString *path = [ZMTools getDomainsPathWithFile:@"123"];
BOOL isSuc = [NSKeyedArchiver archiveRootObject:coding toFile:path];
if (isSuc == YES) {
self.testLabelText = @"归档成功, 点击按钮取出模型中对应的值";
} else {
self.testLabelText = @"归档失败";
}
}
- (void)buttonClick:(UIButton *)sender {
NSString *path = [ZMTools getDomainsPathWithFile:@"123"];
ZMCoding *coding = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
coding.coderID = @"999";
if (sender.tag == 0) {
self.testLabelText = coding.coderID;
} else if (sender.tag == 1) {
self.testLabelText = coding.nickName;
} else {
self.testLabelText = coding.phoneNumber;
}
}
- (NSArray *)buttonTitleArray {
return @[@"ID", @"昵称", @"手机号"];
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )