objc/runtime 探索(四)

前言 在这一篇中,我们来聊一聊runtime中method 定义 先来看一下method相关的定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef struct objc_method *Method; typedef struct objc_selector *SEL; typedef void (*IMP)(void /* id, SEL, ... */ ); //方法描述 struct objc_method_description { SEL name; //方法名称 char *types; //参数类型字符串 }; //以下代码是 ObjC2.0 之前method的定义 struct objc_method { SEL method_name; char *method_types; IMP method_imp; } 里边有三个类型别名,在这儿先解释一下...

August 19, 2014

objc/runtime 探索(三)

前言 续前,本文主要谈论类中的变量和属性 1.Ivar 1.1 Ivar的类型 typedef objc_ivar * Ivar; 1 2 3 4 5 6 7 8 struct objc_ivar { char *ivar_name; //ivar名称 char *ivar_type; //ivar类型 int ivar_offset; //ivar偏移量 #ifdef __LP64__ int space; #endif }//ObjC2.0 已过时 Ivar是objc_ivar的指针,包含变量名称,变量类型等成员. 1.2 为类添加Ivar 运行时规定,只能在objc_allocateClassPair与objc_registerClassPair两个函数之间为类添加变量 如下所示: 1 2 3 4 5 6 7 8 //额外空间 未知,通常设置为 0 Class clazz = objc_allocateClassPair(父类class,类名,额外空间); //以NSString*为例 //变量size sizeof(NSString) //对齐 指针类型的为log2(sizeof(NSString*)) //类型 @encode(NSString*) BOOL flag = class_addIvar(clazz,变量名,变量size,对齐,类型); objc_registerClassPair(clazz); 1....

August 18, 2014

objc/runtime 探索(二)

前言 接上次的话题,继续就objc/runtime进行讨论. 经过今晚的探究,基本掌握了运行时的函数规则,总的有如下几种函数前缀. 在后边的文章中我会就一些问题再次讨论,本文权当做API的速查手册使用. objc_ class_ object_ method_ property_ protocol_ ivar_ ,sel_ ,imp_ 1.objc_xxx 系列函数 函数名称 函数作用 objc_getClass 获取Class对象 objc_getMetaClass 获取MetaClass对象 objc_allocateClassPair 分配空间,创建类(仅在 创建之后,注册之前 能够添加成员变量) objc_registerClassPair 注册一个类(注册后方可使用该类创建对象) objc_disposeClassPair 注销某个类 objc_allocateProtocol 开辟空间创建协议 objc_registerProtocol 注册一个协议 objc_constructInstance 构造一个实例对象(ARC下无效) objc_destructInstance 析构一个实例对象(ARC下无效) objc_setAssociatedObject 为实例对象关联对象 objc_getAssociatedObje*ct 获取实例对象的关联对象 objc_removeAssociatedObjects 清空实例对象的所有关联对象 objc_msgSend 发送ObjC消息 objc_系列函数关注于宏观使用,如类与协议的空间分配,注册,注销等操作...

August 17, 2014

objc/runtime 探索(一)

前言 最近闲来无事,打算对objc/runtime进行一番研究,今晚把API翻了一遍,拿出了如下的一些有趣代码,本代码需事先导入部分objc/runtime中的头文件,如下所示 1 2 #import <objc/runtime.h> #import <objc/message.h> 动态创建类 类的创建分为两步,添加成员变量需要在这两步操作之间,添加成员方法则无此要求 Class objc_allocateClassPair(Class superClass,const char* className,size_t extraBytes); void objc_registerClassPair(Class cls); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //开始类的定义 Class Test= objc_allocateClassPair([NSObject class], "Test", 0); //为类添加变量 class_addIvar(Test, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*)); //为类添加方法 //1.注册名为 test: 的方法 SEL s = sel_registerName("test:"); //2.定义函数实现,此处的IMP是函数指针,原型为 typedef id (*IMP)(id, SEL, ....

August 15, 2014

变长参数的讨论

前言 一直以来对变长参数都比较好奇,索性花时间解解痒. 先从数组指针来看 void test1(int* arr,int count) { int sum = 0; for (int *p = arr; p < arr + count; p++) { sum = sum+ *p; } printf("%d\n",sum ); } 代码较为简单,此处不再展开. 接下来是可变参数 可变参数需要使用 <stdarg.h> 头文件,请提前导入 什么是可变参数? 可变参数是函数或方法中形如int show(int a,...)这样的的参数 可变参数的相关规定 ... 不能单独出现,且只能放在参数列表中的最后 变长参数的类型没有限定,使用时最好统一类型,避免类型安全问题 变长参数的个数没有限定,调用时一般以0x00即 NULL/nil结尾 void test2(int a,...) { int sum = a; //声明变长参数列表vl va_list vl; //va_start :将变长参数的前一个参数的地址存入vl va_start(vl,a); int next; //va_arg :将vl向后偏移sizeof(int),并返回偏移后长度为sizeof(int)的数据 //判断 next 是否为 0x00 ,如果不为 0x00 则继续遍历 while( (next = va_arg(vl,int)) ) { sum+= next; } //释放变长参数列表vl va_end(vl); printf("%d\n",sum ); } 调用形式 int main(int argc,char * args[]) { int arr[] = {1,2,3,4,5}; //数组指针 test1(arr,5); printf("---------\n"); //变长参数 test2(1,2,3,4,5,6,NULL); return 0; } 输出结果 15 --------- 21 小结 ....

August 15, 2014

数据结构--栈

栈 栈是一种数据结构,其特点是后进先出(LIFO).计算机中有很多操作都采用了栈的数据结构,如浏览器的后退,文本编辑的撤销操作等. 栈的定义 栈的额定大小 栈的实际大小 栈的数据 栈的操作 入栈 出栈 清空栈 初始化栈 输出栈内容 栈的应用 中缀转后缀 从左到右遍历中缀表达式: 遇到数值时,直接输出; 遇到操作符时: 如果栈为空或操作符为(,则操作符入栈 如果操作符为),则一直弹栈到与其匹配的(为止 如果栈不为空,则将栈顶操作符与当前操作符进行比较: 如果当前操作符的优先级小于等于栈顶符号的优先级,则将栈顶操作符出栈输出,然后再次拿栈顶符号与当前操作符进行比较; 如果当前操作符的优先级大于栈顶操作符的优先级,则将当前操作符入栈; 直到表达式结束 后缀表达式的计算: 从左到右遍历表达式: 遇到数值直接进栈; 遇到操作符时,取出栈顶的两个元素进行计算,将计算结果入栈 直到表达式结束

July 28, 2014

指针与二维数组

通过前面的学习,已知: int arr[3]={1,3,5}; arr[0] 可以用 *(arr+0) 表示 arr[1] 可以用 *(arr+1) 表示 arr[2] 可以用 *(arr+2) 表示 现有二维数组如下: int arr[3][4]= { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} }; 则: arr[0] ~~~ *(arr+0) arr[1] ~~~ *(arr+1) arr[2] ~~~ *(arr+2) 此处arr[0]即*(arr+0)是二维数组arr的第一个元素的值,该值是内层数组{1,2,3,4}的起始地址. 将arr[0]赋值给 temp数组 int temp0[4] = arr[0]; int temp1[4] = arr[1]; int temp2[4] = arr[2]; 获取最终的值: arr[0][0] ~~~~ 将arr[0]换为上边的temp0数组 arr[0][0] ~~~~ temp0[0] ~~~~ *(temp+0) ~~~~ *(*(arr+0)+0) arr[0][1] ~~~~ temp0[1] ~~~~ *(temp+1) ~~~~ *(*(arr+0)+1) arr[0][2] ~~~~ temp0[2] ~~~~ *(temp+2) ~~~~ *(*(arr+0)+2) arr[0][3] ~~~~ temp0[3] ~~~~ *(temp+3) ~~~~ *(*(arr+0)+3) arr[1][0] ~~~~ 将arr[1]换为上边的temp1数组...

July 24, 2014

Objective-C 正则表达式的使用

前言: 编程中,对于字符串的处理是无处不在的.时常需要在一堆乱码中找到有用的信息.比如在如下的字符串中获取有效的 URL 或 Email ababsdbasbdabdhttp://baidu.com <div>test@test.com<div> 诸如此类问题,我们都可以通过正则表达式来解决,正则在任意编程语言都有对应实现. iOS4 之后,Cocoa 也提供了用于正则的 NSRegularExpression 和 NSTextCheckingResult 这两个类. 前者用于创建正则,匹配,替换;后者是一个快速匹配对象,可用于检查拼写,URL等,也可作为正则匹配的结果,包含匹配到子串所在 range 和匹配规则. 创建: NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern:@"http[s]?://[A-z0-9.-]*" options:NSRegularExpressionCaseInsensitive error:nil]; 正则匹配模式 [] 表示集合, * 表示 出现零次或多次, ? 表示出现零次或一次, http[s]?://[A-z0-9.-]* ,即匹配以 http:// 或 https:// 开头,含有零或多个字母 - . 的字符串 option NSRegularExpressionCaseInsensitive //忽略大小写 NSRegularExpressionAllowCommentsAndWhitespace //忽略空格,回车,tab,注释 NSRegularExpressionIgnoreMetacharacters //忽略 Meta NSRegularExpressionDotMatchesLineSeparators //允许使用 . 通配任何符号,包括换行符 NSRegularExpressionAnchorsMatchLines //允许使用 ^ $匹配行头和行尾 NSRegularExpressionUseUnixLineSeparators //只把 \n 当做换行符 NSRegularExpressionUseUnicodeWordBoundaries 应用: NSRegularExpression 提供了对字符串 匹配 , 替换 的函数,...

May 29, 2014

在iOS开发中使用block

今天下午把启动了一周的项目重构了一下,用storyboard做界面(之前是代码+xib),block做回调(之前是delegate)。 关于storyboard和block都已经出现好几个年头了,从开发效率上来看,这俩东西再配合ARC上简直是倚天屠龙。 在之前的项目中,回调需要做以下几步: A类中声明ProtocolA协议及协议下的方法。 A类中声明delegate属性。 B类实现ProtocolA协议以及协议下的方法,并将自身对象赋值给A类对象的delegate属性。 当需要实现回调的时候,直接通过A类对象的delegate属性调用 ProtocolA协议下的方法即可执行B类中对该协议方法的实现。 以前大伙都在这样乐呵乐呵的写,但是有了block之后,步骤变得简单了许多。 用block做回调省了哪些事儿? 不用定义协议及协议方法。 减少冗余,即不需要为回调而创建B并实现协议方法。 闭包特性,使的代码更为紧凑,表达更为直观,即不需要把业务逻辑分散到多个方法中。 废话少说,上代码。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #import <Foundation/Foundation....

January 13, 2014

函数指针和类成员指针

函数指针和类成员指针经常被用作参数进行传递,多态和函数重载都会用到. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream>#include <string>#include <string.h>using namespace std; class Demo { public: void show() { cout << "member method Demo::show() did call" << endl; str = "str from Demo::show()"; } string str; }; void show() { cout<<"普通函数指针被调用"<<endl; } int main(int argc, char* argv[]) { //普通函数指针 //将普通函数show的地址赋值给pPainShow,优先级原因,必须在(*pPainShow)外加小括号, //否则编译器会将此认为是一个void* pPainShow(); 的函数声明,显然,我们不是这个意思....

November 3, 2013