您的位置:首页 > 其它

runtime-objc_msgSend

2017-01-18 19:44 253 查看
[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
这个问题我现在还没找到解决的办法.

上面的不行,我去创建了一个新工程:



这里选择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:



运行到这里就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


UIAlertAction
title
是个只读属性,但是我想在弹窗弹出,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);
这行代码敲就可以了!

嗯..
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  runtime