1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/chenzm_186-runtime

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Runtime: подробности и применение

1. Механизм работы во время выполнения

  • Динамический язык: тип данных переменной определяется во время компиляции.
  • Статический язык: тип данных, которые становятся известны во время выполнения программы, определяется в это же время.
  1. Динамический язык: Objective-C — это динамический язык, который имеет следующие преимущества: код программирования более гибкий, например, можно преобразовать сообщение в нужный объект или произвольно поменять реализацию метода.
  2. Компилятор + операционная система во время выполнения: поскольку Objective-C является динамическим языком, для его выполнения требуется компилятор и операционная система времени выполнения, которая называется Objc Runtime (Runtime). Runtime — это библиотека, написанная на C и ассемблере, которая позволяет языку C иметь возможности объектно-ориентированного программирования.
  3. Разница в вызове функций: в языке C во время компиляции определяется, какая функция будет вызвана; в языке Objective-C это происходит во время выполнения.
  4. Ошибки на этапе компиляции: в Objective-C можно вызвать любую функцию, даже если она не реализована, и ошибки не будет; в C вызов нереализованной функции приведёт к ошибке.

2. Применение

  • Получение всех переменных-членов класса.
  • Получение всех свойств класса.
  • Получение всех методов класса.
  • Замена реализации метода.
  • Добавление переменной-члена во время выполнения.
  • Добавление свойства во время выполнения.
  • Преобразование словаря в модель.
  • Архивация и разархивация runtime.

Рисунок 1

Демо для скачивания

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 // Используется при преобразовании трёхмерного массива

  • (NSDictionary *)arrayContainModelClass; @end

@interface NSObject (NNKeyValues)

/** Преобразование словаря в модель **/

  • (instancetype)modelWithDict:(NSDictionary *)dict; @end

```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 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/chenzm_186-runtime.git
git@api.gitlife.ru:oschina-mirror/chenzm_186-runtime.git
oschina-mirror
chenzm_186-runtime
chenzm_186-runtime
master