runtime-objc_msgSend

来源:互联网 发布:ip地址和端口的关系 编辑:程序博客网 时间:2024/06/05 04:07

[TOC]
前言: 本文十分基础

这篇文章讲讲怎么用objc_msgSend,配合一个小小的案例,不光光讲怎样用,我会把我是如何学到objc_msgSend这一系列的过程.很多时候,我们看别人的文章,只会给你将结果,很少会讲这个结果究竟是怎样得出来的,OK,言归正传.

runtime,大家都知道这个运行时库,在运行的时候,会把OC代码转为C,然后执行.OK,熟记于心,怎样转?我不会,那么objc_msgSend我也知道这个API,是runtime库里面的,可是大家是如何知道的?我不知道.

起因

最近在看一本书,书上讲到把OC代码转换为C源码,这里有个终端命令clang -rewrite-objc <filename.m>我试着在终端上敲如下命令:
终端
中间打红的部分是路径,最简单的办法就是把.m文件直接拖到终端.我敲下回车,发现报错,'UIKit/UIKit.h' file not found这个问题我现在还没找到解决的办法.

上面的不行,我去创建了一个新工程:
macOS
这里选择Command Line Tool.

然后这里只有一个main.m文件,我在这个文件里面添了简单的几行代码,main.m文件看起来是下面这个样子:

#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {    @autoreleasepool {        NSString * str = @"123";        [str stringByAppendingString:@"456"];    }    return 0;}

OK,我这里有个细节,就是在桌面创建了一个名为clang的文件夹,如果你直接打开终端,那么你的所在的路径是在根目录下的用户.为什么要说这个,因为你使用clang -rewrite-objc <filename.m>这个命令,它会在当前目录下生产一个main.cpp的文件.这个就是我们的目标文件.
首先打开终端,输入如命令clang -rewrite-objc main.m,这里我直接把工程里面的main.m文件复制到桌面的clang文件夹里面了,然后按下回车,我这里会输出很多warning:
finish
运行到这里就OK了,我们可以用open .命令打开clang文件夹,会看到里面多了个main.m文件.

我这里生成的main.m文件有3M大,我猜想是把Foundation框架都转成了C源码.9.5W行代码��.
直接command+↓跳到文件的最下面,可以看到int main()这个main函数,函数体内,就是我关注的东西了.
我是如何找到这个main函数在最下面?是我看书上的图片,显示的就是int main(...,我就直接搜索了一下这个int main的位置,代码如下:

int main(int argc, const char * argv[]) {    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;         NSString * str = (NSString *)&__NSConstantStringImpl__var_folders_6q_7tzk06yx4fd147m641v6dnsw0000gn_T_main_4f05b0_mi_0;        ((NSString *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)str, sel_registerName("stringByAppendingString:"), (NSString *)&__NSConstantStringImpl__var_folders_6q_7tzk06yx4fd147m641v6dnsw0000gn_T_main_4f05b0_mi_1);    }    return 0;}

OK,到了这一步,C语言的源码暴露在我们面前了,到这里,就可以比葫芦画瓢,试着用objc_msgSend来做一些没有objc_msgSend它不能做的事情咯.

#import "ViewController.h"#import "UIAlertAction+SLAlertAction.h"#import <objc/message.h>@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    __block UIAlertController *alertVc = [self tipWithTitle:@"你好吗" Message:@"我不好"];    [self presentViewController:alertVc animated:YES completion:nil];    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        NSLog(@"aaa");        alertVc.title = @"heelo";        ((UIAlertAction *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)alertVc.actions.firstObject, @selector(setTitle:), @"hello");    });}- (UIAlertController *)tipWithTitle:(NSString *)title Message:(NSString *)message{    UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];    UIAlertAction *firm = [UIAlertAction sl_alertActionWithType:HYBVerifyAlertActionConfirm navController:nil refuseMessage:nil];    UIAlertAction *cancel = [UIAlertAction sl_alertActionWithType:HYBVerifyAlertActionCancel navController:nil refuseMessage:nil];    [alertVc addAction:cancel];    [alertVc addAction:firm];    return alertVc;}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}@end

UIAlertActiontitle是个只读属性,但是我想在弹窗弹出,3秒后进行修改取消两字为heelo.这里我用了一个UIAlertAction的分类.其实就是添加了一个取消一个确定按钮.
我们可以使用objc_msgSend来修改.
只需要对比着((NSString *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)str, sel_registerName("stringByAppendingString:"), (NSString *)&__NSConstantStringImpl__var_folders_6q_7tzk06yx4fd147m641v6dnsw0000gn_T_main_4f05b0_mi_1);这行代码敲就可以了!

嗯..

0 0
原创粉丝点击