1003.Emergency(25)

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (<= 500) – the number of cities (and the cities are numbered from 0 to N-1), M – the number of roads, C1 and C2 – the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.

Output

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather.

All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input

5 6 0 2

1 2 1 5 3

0 1 1

0 2 2

0 3 1

1 2 1

2 4 1

3 4 1

Sample Output

2 4

AC参考代码:

#include <stdio.h>
#include <stdlib.h>

const int MAX = 501;	//The max number of cities
const int MAX_INT =  9999;
int path[MAX][MAX],isVisited[MAX],weight[MAX];	//The  variable about Cities
int minDist,maxWeight,cnt,cities;	//The variable of recording the boundary value

void initGraph(int cities)
{
	for(int i=0;i<cities;i++){
		isVisited[i] = 0;	//The value of 0 means not visited 
		for(int j=0;j<cities;j++){
			path[i][j] = MAX_INT;	//The initial length of path is infinite 
		} 
	}
}

void DFS(int start,const int dest,int dist,int weit)
{
	if(start == dest){	//The boundary value
		if(dist < minDist){	//There is new minist path 
			minDist = dist;
			cnt = 1; 	//Init the new recording value
			maxWeight = weit;
		}
		else if(dist == minDist){	//More minist path
			cnt++;
			if(maxWeight < weit){
				maxWeight = weit;
			}
		}
		return;
	}
	if(dist > minDist)	return;	//pruning,optimise the algorithm
	
	for(int i=0;i<cities;i++){
		if(isVisited[i]==0 && path[start][i]!=MAX_INT){		//The city is not visited and there is a path bewteen two cites
			isVisited[i] = 1;
			DFS(i,dest,dist+path[start][i],weit+weight[i]);
			isVisited[i] = 0;	//When you back,this node could be one of another path,so remark it.
		}
	}
}

int main()
{
	int N,M,c1,c2,x,y,temp;
	scanf("%d %d %d %d",&N,&M,&c1,&c2);
	cities = N; minDist = MAX_INT,cnt = 0;
	initGraph(cities);
	
	for(int i=0;i<N;i++){
		scanf("%d",&weight[i]);
	}
	
	for(int i=0;i<M;i++){
		scanf("%d %d %d",&x,&y,&temp);
		path[x][y] =path[y][x] = temp;
	}	//Finish data input 
	
	DFS(c1,c2,0,weight[c1]);
	
	printf("%d %d",cnt,maxWeight);
	return 0;
}

此题主要考的是DFS深度优先遍历+递归的考点,涉及的变量相对多点比较综合

注意点:

1:在做DFS的时候临界点要注意:

    1、找到最短路径,更新所有信息

    2、找到同样长度的最短路径,更新cnt的计数值,同时对weight作判断

    3、其他直接return 就ok

2:if(dist > minDist) return; 实现搜索的剪枝,优化代码

3:还有初始化图的时候 path[x][y] =path[y][x] 要完全初始化

4:DFS(i,dest,dist+path[start][i],weit+weight[i]); isVisited[i] = 0;

    DFS完子路径的时候回退的时候需要将i结点重置为未访问,因为在图中深度优先遍历的时候下次可能再次用到。

其他没什么注意的,水水过了

[JWFD开源工作流]JWFDv0.96.6的升级问题

*  那么JWFD下一个版本v96.6的升级的难度,就集中在如何解决下面三个问题上面

*

*  v966a最大限度增加反馈循环的次数的问题 (这个问题解决了,自定义编译器的难点也基本解决了)

*  v966b数据与指令双线性同步控制问题 (这个问题解决了,JAVA版本的3D图形编辑器的问题基本也解决了)

*  v966c前端UI与引擎的控制参数的链接问题 (这个解决了,用户的体验感和设计效率将极大提高)

*

*  解决上面的问题首先需要完成几个前导课题:

*

*    归纳和总结出,在一个流程运行过程中,流程中传递的数据与流程的控制指令之间的数学关系

*    学习底层控制语言并总结出,如何在一个被上层代码封锁的内存区中,清除多余的内存变量和内存链接

*    专研并熟悉JAVA SWING技术

*

*  如果完成上面的课题和研究,那么JWFD工程代码的应用层次和水平又将上一个台阶

12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!

12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!

1.gets()函数

问:请找出下面代码里的问题:

#include<stdio.h>

int main(void)

{

    char buff[10];

    memset(buff,0,sizeof(buff));

    gets(buff);

    printf(“\n The buffer entered is [%s]\n”,buff);

    return 0;

}

答:上面代码里的问题在于函数gets()的使用,这个函数从stdin接收一个字符串而不检查它所复制的缓存的容积,这可能会导致缓存溢出。这里推荐使用标准函数fgets()代替。

2.strcpy()函数

问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?

#include<stdio.h>

int main(int argc, char *argv[])

{

    int flag = 0;

    char passwd[10];

    memset(passwd,0,sizeof(passwd));

    strcpy(passwd, argv[1]);

    if(0 == strcmp(“LinuxGeek”, passwd))

    {

        flag = 1;

    }

    if(flag)

    {

        printf(“\n Password cracked \n”);

    }

    else

    {

        printf(“\n Incorrect passwd \n”);

    }

    return 0;

}

答:破解上述加密的关键在于利用攻破strcpy()函数的漏洞。所以用户在向“passwd”缓存输入随机密码的时候并没有提前检查“passwd”的容量是否足够。所以,如果用户输入一个足够造成缓存溢出并且重写“flag”变量默认值所存在位置的内存的长“密码”,即使这个密码无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。例如:

$ ./psswd aaaaaaaaaaaaa

Password cracked

虽然上面的密码并不正确,但我们仍然可以通过缓存溢出绕开密码安全保护。

要避免这样的问题,建议使用 strncpy()函数。

作者注:最近的编译器会在内部检测栈溢出的可能,所以这样往栈里存储变量很难出现栈溢出。在我的gcc里默认就是这样,所以我不得不使用编译命令‘-fno-stack-protector’来实现上述方案。

3.main()的返回类型

问:下面的代码能 编译通过吗?如果能,它有什么潜在的问题吗?

#include<stdio.h>

void main(void)

{

    char *ptr = (char*)malloc(10);

    if(NULL == ptr)

    {

        printf(“\n Malloc failed \n”);

        return;

    }

    else

    {

        // Do some processing

        free(ptr);

    }

    return;

}

答:因为main()方法的返回类型,这段代码的错误在大多数编译器里会被当作警告。main()的返回类型应该是“int”而不是“void”。因为“int”返回类型会让程序返回状态值。这点非常重要,特别当程序是作为依赖于程序成功运行的脚本的一部分运行时。

4.内存泄露

问:下面的代码会导致内存泄漏吗?

#include<stdio.h>

void main(void)

{

    char *ptr = (char*)malloc(10);

    if(NULL == ptr)

    {

        printf(“\n Malloc failed \n”);

        return;

    }

    else

    {

        // Do some processing

    }

    return;

}

答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!

提示:如果你想知道更多关于内存泄漏的知识和内存泄漏检测工具,可以来看看我们在Valgrind上的文章。

5.free()函数

问:下面的程序会在用户输入’freeze’的时候出问题,而’zebra’则不会,为什么?

#include<stdio.h>

int main(int argc, char *argv[])

{

    char *ptr = (char*)malloc(10);

    if(NULL == ptr)

    {

        printf(“\n Malloc failed \n”);

        return -1;

    }

    else if(argc == 1)

    {

        printf(“\n Usage  \n”);

    }

    else

    {

        memset(ptr, 0, 10);

        strncpy(ptr, argv[1], 9);

        while(*ptr != ‘z’)

        {

            if(*ptr == ”)

                break;

            else

                ptr++;

        }

        if(*ptr == ‘z’)

        {

            printf(“\n String contains ‘z’\n”);

            // Do some more processing

        }

       free(ptr);

    }

    return 0;

}

答:这里的问题在于,代码会(通过增加“ptr”)修改while循环里“ptr”存储的地址。当输入“zebra”时,while循环会在执行前被终止,因此传给free()的变量就是传给malloc()的地址。但在“freeze”时,“ptr”存储的地址会在while循环里被修改,因此导致传给free()的地址出错,也就导致了seg-fault或者崩溃。

6.使用_exit退出

问:在下面的代码中,atexit()并没有被调用,为什么?

#include<stdio.h>

void func(void)

{

    printf(“\n Cleanup function called \n”);

    return;

}

int main(void)

{

    int i = 0;

    atexit(func);

    for(;i<0xffffff;i++);

    _exit(0);

}

这是因为_exit()函数的使用,该函数并没有调用atexit()等函数清理。如果使用atexit()就应当使用exit()或者“return”与之相配合。

7.void*和C结构体

问:你能设计一个能接受任何类型的参数并返回interger(整数)结果的函数吗?

答:如下:

int func(void *ptr)

如果这个函数的参数超过一个,那么这个函数应该由一个结构体来调用,这个结构体可以由需要传递参数来填充。

8.*和++操作

问:下面的操作会输出什么?为什么?

#include<stdio.h>

int main(void)

{

    char *ptr = “Linux”;

    printf(“\n [%c] \n”,*ptr++);

    printf(“\n [%c] \n”,*ptr);

    return 0;

}

答:输出结果应该是这样:

[L] 

[i]

因为“++”和“*”的优先权一样,所以“*ptr++”相当于“*(ptr++)”。即应该先执行ptr++,然后才是*ptr,所以操作结果是“L”。第二个结果是“i”。

9.问:修改代码片段(或者只读代码)

问:下面的代码段有错,你能指出来吗?

#include<stdio.h>

int main(void)

{

    char *ptr = “Linux”;

    *ptr = ‘T’;

    printf(“\n [%s] \n”, ptr);

    return 0;

}

答:这是因为,通过*ptr = ‘T’,会改变内存中代码段(只读代码)“Linux”的第一个字母。这个操作是无效的,因此会造成seg-fault或者崩溃。

10.会改变自己名字的进程

问:你能写出一个在运行时改变自己进程名的程序吗?

答:参见下面这段代码:

#include<stdio.h>

int main(int argc, char *argv[])

{

    int i = 0;

    char buff[100];

    memset(buff,0,sizeof(buff));

    strncpy(buff, argv[0], sizeof(buff));

    memset(argv[0],0,strlen(buff));

    strncpy(argv[0], “NewName”, 7);

    // Simulate a wait. Check the process

    // name at this point.

    for(;i<0xffffffff;i++);

    return 0;

}

11.返回本地变量的地址

问:下面代码有问题吗?如果有,该怎么修改?

#include<stdio.h>

int* inc(int val)

{

  int a = val;

  a++;

  return &a;

}

int main(void)

{

    int a = 10;

    int *val = inc(a);

    printf(“\n Incremented value is equal to [%d] \n”, *val);

    return 0;

}

答:尽管上面的程序有时候能够正常运行,但是在“inc()”中存在严重的漏洞。这个函数返回本地变量的地址。因为本地变量的生命周期就是“inc()”的生命周期,所以在inc结束后,使用本地变量会发生不好的结果。这可以通过将main()中变量“a”的地址来避免,这样以后还可以修改这个地址存储的值。

12.处理printf()的参数

问:下面代码会输出什么?

#include<stdio.h>

int main(void)

{

    int a = 10, b = 20, c = 30;

    printf(“\n %d..%d..%d \n”, a+b+c, (b = b*2), (c = c*2));

    return 0;

}

答:输出结果是:

110..40..60

这是因为C语言里函数的参数默认是从右往左处理的,输出时是从左往右。

汇编写启动代码之设置栈和调用C语言

本文使用的开发板是九鼎创展的X210 iNand版本。

 

一、预备知识

 

1、C语言运行时和栈

(1)C语言运行时需要

C语言运行时(runtime)需要一定的条件,这些条件由汇编来提供。C语言运行时主要是需要栈。

 

(2)C语言与栈的关系

C语言中的局部变量都是用栈来实现的,如果汇编部分没有给C语言预先设置合理合法的栈地址,那么C代码中定义的局部变量就会落空,整个C程序就不能运行。

 

2、CPU模式和各种模式下的栈

(1)在ARM的37个寄存器中,每种模式下都有自己的独立的SP寄存器(r13)。

(2)我们要设置栈,不可能而且也没有必要去设置所有的栈。我们先要找到当前的模式,然后设置该模式下的栈到合理合法的位置即可。

(3)系统在复位后默认是进入SVC模式。

(4)我们先把模式设置为SVC,再直接操作SP,即可访问SVC模式下的SP。因为复位后就已经是SVC模式了,所以直接设置SP即可。

 

3、设置栈指针

(1)栈必须是当前一段可用的内存,这个内存必须是被初始化过可以访问的内存,而且这个内存只会被用作栈,不会被其他程序占用。

(2)当前CPU刚复位,外部的DRAM尚未初始化,目前可用的内存只有内部的SRAM(不需初始化即可使用),因此只能在SRAM中找一段内存来作为SVC的栈。

(3)在ARM中,ATPCS要求使用满减栈

 

二、查阅iROM Application Note文档

 

 

由上图可知SVC栈应该设置为0xD0037D80。

 

三、代码实现

 

#define SVC_STACK 0xD0037D80

.global _start					
_start:
	ldr sp, =SVC_STACK
	
    b .

 

四、汇编调用C函数,实现LED闪烁效果

 

(1)led.c

#define GPJ0CON	 0xE0200240
#define GPJ0DAT	 0xE0200244

#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)

void delay(void);

void led_blink(void) {
	rGPJ0CON = 0x11111111;
	
	while(1) {
		// led亮
		rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
		// 延时
		delay();
		// led灭
		rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
		// 延时
		delay();
	}
}

void delay(void) {
	volatile unsigned int i = 900000;		
	while (i--);							
}

 

(2)Makefile

led.bin: start.o led.o
	arm-linux-ld -Ttext 0x0 -o led.elf $^
	arm-linux-objcopy -O binary led.elf led.bin
	arm-linux-objdump -D led.elf > led_elf.dis
	gcc mkv210_image.c -o mkx210
	./mkx210 led.bin 210.bin
	
%.o : %.S
	arm-linux-gcc -o $@ $< -c -nostdlib

%.o : %.c
	arm-linux-gcc -o $@ $< -c -nostdlib

clean:
	rm *.o *.elf *.bin *.dis mkx210 -f

 

(3)start.S

#define SVC_STACK	0xD0037D80

.global _start					
_start:
	ldr sp, =SVC_STACK

	bl led_blink					
	
	b .

 

S5PV210之重定位

本文使用的开发板是九鼎创展的X210 iNand版本。

 

本文要完成的功能是:在SRAM中将代码从0xd0020010重定位到0xd0024000(本来代码是运行在0xd0020010的,但我们又希望代码实际是在0xd0024000位置运行的,这时就需要重定位了)。

 

一、思路分析

 

(1)通过链接脚本将代码链接到0xd0024000

(2)dnw下载时将bin文件下载到0xd0020010

(3)代码执行时,通过代码前段的少量PIC位置无关码将整个代码搬移到0xd0024000

(4)使用一个长跳转,跳转到0xd0024000处的代码继续执行,重定位完成

 

通过(1)和(2)就保证了代码实际下载运行在0xd0020010,但是却被链接在0xd0024000,从而为重定位奠定了基础。当我们把代码链接地址设置为0xd0024000时,实际隐含意思就是这个代码将来必须放在0xd0024000位置才能正确执行。如果实际运行地址不是0xd0024000就要出事(除非代码是PIC位置无关码)。

 

当我们执行完代码重定位后,实际上在SRAM中有2份代码的镜像:一份是我们下载到0xd0020010处开头的,另一份是重定位代码复制到0xd0024000处开头的,这两份内容完全相同,仅仅地址不同。重定位之后使用ldr pc, =led_blink这句长跳转直接从0xd0020010处的代码跳转到0xd0024000开头的那份代码的led_blink函数去执行(实际上此时在SRAM中有2个led_blink函数的镜像,两个都能执行。如果短跳转bl led_blink则执行的就是0xd0020010开头的这一份,如果长跳转ldr pc, =led_blink则执行的就是0xd0024000开头的这一份)。

 

当链接地址和运行地址相同时,短跳转和长跳转实际效果是一样的。当链接地址和运行地址不同时,短跳转实际执行的是运行地址处的那一份代码,而长跳转执行的是链接地址处的那一份代码。

 

通过以上信息,就知道重定位代码的作用是:在PIC位置无关码执行完之前(代码中第一句位置有关码执行之前)必须将整个代码搬移到0xd0024000位置去执行。

 

二、代码实现

 

1、Makefile

 

led.bin: start.o led.o
	arm-linux-ld -Tlink.lds -o led.elf $^
	arm-linux-objcopy -O binary led.elf led.bin
	arm-linux-objdump -D led.elf > led_elf.dis
	gcc mkv210_image.c -o mkx210
	./mkx210 led.bin 210.bin
	
%.o : %.S
	arm-linux-gcc -o $@ $< -c -nostdlib

%.o : %.c
	arm-linux-gcc -o $@ $< -c -nostdlib

clean:
	rm *.o *.elf *.bin *.dis mkx210 -f

 

2、链接脚本(link.lds)

 

SECTIONS
{
	. = 0xd0024000;
	
	.text : {
		start.o
		* (.text)
	}
    		
	.data : {
		* (.data)
	}
	
	bss_start = .; 
	.bss : {
		* (.bss)
	}
	
	bss_end  = .;	
}

 

3、start.S

 

#define SVC_STACK	0xD0037D80

.global _start					
_start:
	// 设置SVC栈
	ldr sp, =SVC_STACK
	
	// 重定位
	// adr指令用于加载_start当前的运行地址
	adr r0, _start  				
	// ldr指令用于加载_start的链接地址:0xd0024000
	ldr r1, =_start 	
	// bss段的起始地址
	ldr r2, =bss_start   // 重定位代码的结束地址,重定位只需重定位代码段和数据段即可
	// 比较_start的运行地址和链接地址是否相等
	cmp r0, r1	
	// 如果相等说明不需要重定位,所以跳过copy_loop,直接到clean_bss		
	beq clean_bss		

// 用汇编实现的一个while循环
copy_loop:
	ldr r3, [r0], #4   // 源
	str r3, [r1], #4   // 目标  这两句代码就完成了4个字节内容的拷贝
	cmp r1, r2	       // r1和r2都是用ldr加载的,都是链接地址,所以r1不断+4总能等于r2
	bne copy_loop

// 清bss段,其实就是在链接地址处把bss段全部清零
clean_bss:
	ldr r0, =bss_start					
	ldr r1, =bss_end
	cmp r0, r1	        // 如果r0等于r1,说明bss段为空
	beq run_on_dram	    // 清除完bss之后的地址
	mov r2, #0
clear_loop:
	str r2, [r0], #4   // 先将r2中的值放入r0所指向的内存地址,然后r0 = r0 + 4
	bne clear_loop

run_on_dram:	
	// 长跳转到led_blink开始第二阶段
	ldr pc, =led_blink				

	b .

 

4、led.c

 

#define GPJ0CON	 0xE0200240
#define GPJ0DAT	 0xE0200244

#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)

void delay(void);

void led_blink(void) {
	rGPJ0CON = 0x11111111;
	
	while(1) {
		// led亮
		rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
		// 延时
		delay();
		// led灭
		rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
		// 延时
		delay();
	}
}

void delay(void) {
	volatile unsigned int i = 900000;		
	while (i--);							
}

 

重定位就是汇编代码中的copy_loop函数,代码的作用是使用循环结构来逐句复制代码到链接地址。复制的源地址是SRAM的0xd0020010,复制的目标地址是SRAM的0xd0024000,复制的长度是bss_start – _start,所以复制的长度就是整个重定位需要重定位的长度,也就是整个程序中代码段 + 数据段的长度,bss段(bss段是初始化为0的全局变量)不需要重定位。

 

清除bss段是为了满足C语言的运行时要求(C语言要求显式初始化为0的全局变量,或者未显式初始化的全局变量的值为0),实际上C语言编译器就是通过清bss段来实现C语言的这个特性的。一般情况下我们的程序是不需要负责清bss段的,因为C语言编译器和链接器会帮我们的程序自动添加一段头程序,这段程序会在main函数之前运行,这段代码就负责清除bss。但是在我们代码重定位了之后,因为编译器帮我们附加的代码只是帮我们清除了运行地址那一份代码中的bss,而未清除重定位地址处的那一份代码的bss,所以重定位之后需要自己去清除bss。

 

清理完bss段后重定位就结束了。此时的状况是:

(1)当前运行地址还在0xd0020010开头的那份代码中运行着

(2)SRAM中已经有了2份代码,1份在0xd0020010开头,另一份在0xd0024000开头

最后执行ldr pc, =led_blink这句长跳转直接从0xd0020010处的代码跳转到0xd0024000开头的那份代码的led_blink函数去执行。

C语言随笔_类型声明

有位同学说,“老师,我运行如下代码,结果报错了”

#include <iostream.h>

int main(){

char c,

int b;

return 0;

}

报错结果是:

——————–Configuration: Test – Win32 Debug——————–

Compiling…

demo.c

d:\ccode\test\demo.c(4) : error C2059: syntax error : ‘type’

Error executing cl.exe.

demo.obj – 1 error(s), 0 warning(s)

这位同学不知道错在哪里。

其实是错在 char c, int b;了。声明变量c后,紧接着声明变量b,此时变量c后是不能写逗号的,要写分号才行。

可能这位同学的作文水平比较好,逗号、句号用的比较溜。可是计算机代码可不像写作文呀,声明完一种类型的变量后,一定要以分号结尾哦。

mybatis多表查询

孤傲苍狼

只为成功找方法,不为失败找借口!

MyBatis学习总结(五)——实现关联表查询

一、一对一关联

1.1、提出需求

  根据班级id查询班级信息(带老师的信息)

1.2、创建表和数据

  创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关系。

复制代码

1 CREATE TABLE teacher(

2     t_id INT PRIMARY KEY AUTO_INCREMENT,

3     t_name VARCHAR(20)

4 );

5 CREATE TABLE class(

6     c_id INT PRIMARY KEY AUTO_INCREMENT,

7     c_name VARCHAR(20),

8     teacher_id INT

9 );

10 ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id);   

11

12 INSERT INTO teacher(t_name) VALUES(‘teacher1’);

13 INSERT INTO teacher(t_name) VALUES(‘teacher2’);

14

15 INSERT INTO class(c_name, teacher_id) VALUES(‘class_a’, 1);

16 INSERT INTO class(c_name, teacher_id) VALUES(‘class_b’, 2);

复制代码

  表之间的关系如下:

  

1.3、定义实体类

  1、Teacher类,Teacher类是teacher表对应的实体类。

复制代码

1 package me.gacl.domain;

2

3 /**

4  * @author gacl

5  * 定义teacher表对应的实体类

6  */

7 public class Teacher {

8

9     //定义实体类的属性,与teacher表中的字段对应

10     private int id;            //id===>t_id

11     private String name;    //name===>t_name

12

13     public int getId() {

14         return id;

15     }

16

17     public void setId(int id) {

18         this.id = id;

19     }

20

21     public String getName() {

22         return name;

23     }

24

25     public void setName(String name) {

26         this.name = name;

27     }

28

29     @Override

30     public String toString() {

31         return “Teacher [id=” + id + “, name=” + name + “]”;

32     }

33 }

复制代码

  2、Classes类,Classes类是class表对应的实体类

复制代码

1 package me.gacl.domain;

2

3 /**

4  * @author gacl

5  * 定义class表对应的实体类

6  */

7 public class Classes {

8

9     //定义实体类的属性,与class表中的字段对应

10     private int id;            //id===>c_id

11     private String name;    //name===>c_name

12    

13     /**

14      * class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,

15      * 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的

16      */

17     private Teacher teacher;

18

19     public int getId() {

20         return id;

21     }

22

23     public void setId(int id) {

24         this.id = id;

25     }

26

27     public String getName() {

28         return name;

29     }

30

31     public void setName(String name) {

32         this.name = name;

33     }

34

35     public Teacher getTeacher() {

36         return teacher;

37     }

38

39     public void setTeacher(Teacher teacher) {

40         this.teacher = teacher;

41     }

42

43     @Override

44     public String toString() {

45         return “Classes [id=” + id + “, name=” + name + “, teacher=” + teacher+ “]”;

46     }

47 }

复制代码

1.4、定义sql映射文件classMapper.xml

复制代码

1 < xml version=”1.0″ encoding=”UTF-8″  >

2 <!DOCTYPE mapper PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN” “http://mybatis.org/dtd/mybatis-3-mapper.dtd”>

3 <!– 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的

4 例如namespace=”me.gacl.mapping.classMapper”就是me.gacl.mapping(包名)+classMapper(classMapper.xml文件去除后缀)

5  –>

6 <mapper namespace=”me.gacl.mapping.classMapper”>

7

8     <!–

9         根据班级id查询班级信息(带老师的信息)

10         ##1. 联表查询

11         SELECT * FROM class c,teacher t WHERE c.teacher_id=t.t_id AND c.c_id=1;

12        

13         ##2. 执行两次查询

14         SELECT * FROM class WHERE c_id=1;  //teacher_id=1

15         SELECT * FROM teacher WHERE t_id=1;//使用上面得到的teacher_id

16      –>

17

18     <!–

19     方式一:嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集

20              封装联表查询的数据(去除重复的数据)

21         select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=1

22     –>

23     <select id=”getClass” parameterType=”int” resultMap=”ClassResultMap”>

24         select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=#{id}

25     </select>

26     <!– 使用resultMap映射实体类和字段之间的一一对应关系 –>

27     <resultMap type=”me.gacl.domain.Classes” id=”ClassResultMap”>

28         <id property=”id” column=”c_id”/>

29         <result property=”name” column=”c_name”/>

30         <association property=”teacher” javaType=”me.gacl.domain.Teacher”>

31             <id property=”id” column=”t_id”/>

32             <result property=”name” column=”t_name”/>

33         </association>

34     </resultMap>

35    

36     <!–

37     方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型

38         SELECT * FROM class WHERE c_id=1;

39         SELECT * FROM teacher WHERE t_id=1   //1 是上一个查询得到的teacher_id的值

40     –>

41      <select id=”getClass2″ parameterType=”int” resultMap=”ClassResultMap2″>

42         select * from class where c_id=#{id}

43      </select>

44      <!– 使用resultMap映射实体类和字段之间的一一对应关系 –>

45      <resultMap type=”me.gacl.domain.Classes” id=”ClassResultMap2″>

46         <id property=”id” column=”c_id”/>

47         <result property=”name” column=”c_name”/>

48         <association property=”teacher” column=”teacher_id” select=”getTeacher”/>

49      </resultMap>

50     

51      <select id=”getTeacher” parameterType=”int” resultType=”me.gacl.domain.Teacher”>

52         SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}

53      </select>

54

55 </mapper>

复制代码

  在conf.xml文件中注册classMapper.xml

<mappers>

        <!– 注册classMapper.xml文件,

        classMapper.xml位于me.gacl.mapping这个包下,所以resource写成me/gacl/mapping/classMapper.xml–>

        <mapper resource=”me/gacl/mapping/classMapper.xml”/>

</mappers>

1.5、编写单元测试代码

复制代码

1 package me.gacl.test;

2

3 import me.gacl.domain.Classes;

4 import me.gacl.util.MyBatisUtil;

5 import org.apache.ibatis.session.SqlSession;

6 import org.junit.Test;

7

8 public class Test3 {

9    

10     @Test

11     public void testGetClass(){

12         SqlSession sqlSession = MyBatisUtil.getSqlSession();

13         /**

14          * 映射sql的标识字符串,

15          * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,

16          * getClass是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL

17          */

18         String statement = “me.gacl.mapping.classMapper.getClass”;//映射sql的标识字符串

19         //执行查询操作,将查询结果自动封装成Classes对象返回

20         Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录

21         //使用SqlSession执行完SQL之后需要关闭SqlSession

22         sqlSession.close();

23         System.out.println(clazz);//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]

24     }

25    

26     @Test

27     public void testGetClass2(){

28         SqlSession sqlSession = MyBatisUtil.getSqlSession();

29         /**

30          * 映射sql的标识字符串,

31          * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,

32          * getClass2是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL

33          */

34         String statement = “me.gacl.mapping.classMapper.getClass2”;//映射sql的标识字符串

35         //执行查询操作,将查询结果自动封装成Classes对象返回

36         Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录

37         //使用SqlSession执行完SQL之后需要关闭SqlSession

38         sqlSession.close();

39         System.out.println(clazz);//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]

40     }

41 }

复制代码

1.6、MyBatis一对一关联查询总结

  MyBatis中使用association标签来解决一对一的关联查询,association标签可用的属性如下:

property:对象属性的名称

javaType:对象属性的类型

column:所对应的外键字段名称

select:使用另一个查询封装的结果

二、一对多关联

2.1、提出需求

  根据classId查询对应的班级信息,包括学生,老师

2.2、创建表和数据

  在上面的一对一关联查询演示中,我们已经创建了班级表和教师表,因此这里再创建一张学生表

复制代码

CREATE TABLE student(

    s_id INT PRIMARY KEY AUTO_INCREMENT,

    s_name VARCHAR(20),

    class_id INT

);

INSERT INTO student(s_name, class_id) VALUES(‘student_A’, 1);

INSERT INTO student(s_name, class_id) VALUES(‘student_B’, 1);

INSERT INTO student(s_name, class_id) VALUES(‘student_C’, 1);

INSERT INTO student(s_name, class_id) VALUES(‘student_D’, 2);

INSERT INTO student(s_name, class_id) VALUES(‘student_E’, 2);

INSERT INTO student(s_name, class_id) VALUES(‘student_F’, 2);

复制代码

  

2.3、定义实体类

  1、Student类

复制代码

1 package me.gacl.domain;

2

3 /**

4  * @author gacl

5  * 定义student表所对应的实体类

6  */

7 public class Student {

8

9     //定义属性,和student表中的字段对应

10     private int id;            //id===>s_id

11     private String name;    //name===>s_name

12    

13     public int getId() {

14         return id;

15     }

16

17     public void setId(int id) {

18         this.id = id;

19     }

20

21     public String getName() {

22         return name;

23     }

24

25     public void setName(String name) {

26         this.name = name;

27     }

28

29     @Override

30     public String toString() {

31         return “Student [id=” + id + “, name=” + name + “]”;

32     }

33 }

复制代码

  2、修改Classes类,添加一个List<Student> students属性,使用一个List<Student>集合属性表示班级拥有的学生,如下:

复制代码

1 package me.gacl.domain;

2

3 import java.util.List;

4

5 /**

6  * @author gacl

7  * 定义class表对应的实体类

8  */

9 public class Classes {

10

11     //定义实体类的属性,与class表中的字段对应

12     private int id;            //id===>c_id

13     private String name;    //name===>c_name

14    

15     /**

16      * class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,

17      * 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的

18      */

19     private Teacher teacher;

20     //使用一个List<Student>集合属性表示班级拥有的学生

21     private List<Student> students;

22

23     public int getId() {

24         return id;

25     }

26

27     public void setId(int id) {

28         this.id = id;

29     }

30

31     public String getName() {

32         return name;

33     }

34

35     public void setName(String name) {

36         this.name = name;

37     }

38

39     public Teacher getTeacher() {

40         return teacher;

41     }

42

43     public void setTeacher(Teacher teacher) {

44         this.teacher = teacher;

45     }

46

47     public List<Student> getStudents() {

48         return students;

49     }

50

51     public void setStudents(List<Student> students) {

52         this.students = students;

53     }

54

55     @Override

56     public String toString() {

57         return “Classes [id=” + id + “, name=” + name + “, teacher=” + teacher

58                 + “, students=” + students + “]”;

59     }

60 }

复制代码

2.4、修改sql映射文件classMapper.xml

  添加如下的SQL映射信息

复制代码

1 <!–

2         根据classId查询对应的班级信息,包括学生,老师

3      –>

4     <!–

5     方式一: 嵌套结果: 使用嵌套结果映射来处理重复的联合结果的子集

6     SELECT * FROM class c, teacher t,student s WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND  c.c_id=1

7      –>

8     <select id=”getClass3″ parameterType=”int” resultMap=”ClassResultMap3″>

9         select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and  c.c_id=#{id}

10     </select>

11     <resultMap type=”me.gacl.domain.Classes” id=”ClassResultMap3″>

12         <id property=”id” column=”c_id”/>

13         <result property=”name” column=”c_name”/>

14         <association property=”teacher” column=”teacher_id” javaType=”me.gacl.domain.Teacher”>

15             <id property=”id” column=”t_id”/>

16             <result property=”name” column=”t_name”/>

17         </association>

18         <!– ofType指定students集合中的对象类型 –>

19         <collection property=”students” ofType=”me.gacl.domain.Student”>

20             <id property=”id” column=”s_id”/>

21             <result property=”name” column=”s_name”/>

22         </collection>

23     </resultMap>

24    

25     <!–

26         方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型

27             SELECT * FROM class WHERE c_id=1;

28             SELECT * FROM teacher WHERE t_id=1   //1 是上一个查询得到的teacher_id的值

29             SELECT * FROM student WHERE class_id=1  //1是第一个查询得到的c_id字段的值

30      –>

31      <select id=”getClass4″ parameterType=”int” resultMap=”ClassResultMap4″>

32         select * from class where c_id=#{id}

33      </select>

34      <resultMap type=”me.gacl.domain.Classes” id=”ClassResultMap4″>

35         <id property=”id” column=”c_id”/>

36         <result property=”name” column=”c_name”/>

37         <association property=”teacher” column=”teacher_id” javaType=”me.gacl.domain.Teacher” select=”getTeacher2″></association>

38         <collection property=”students” ofType=”me.gacl.domain.Student” column=”c_id” select=”getStudent”></collection>

39      </resultMap>

40     

41      <select id=”getTeacher2″ parameterType=”int” resultType=”me.gacl.domain.Teacher”>

42         SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}

43      </select>

44     

45      <select id=”getStudent” parameterType=”int” resultType=”me.gacl.domain.Student”>

46         SELECT s_id id, s_name name FROM student WHERE class_id=#{id}

47      </select>

复制代码

2.5、编写单元测试代码

复制代码

1 package me.gacl.test;

2

3 import me.gacl.domain.Classes;

4 import me.gacl.util.MyBatisUtil;

5 import org.apache.ibatis.session.SqlSession;

6 import org.junit.Test;

7

8 public class Test4 {

9    

10     @Test

11     public void testGetClass3(){

12         SqlSession sqlSession = MyBatisUtil.getSqlSession();

13         /**

14          * 映射sql的标识字符串,

15          * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,

16          * getClass3是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL

17          */

18         String statement = “me.gacl.mapping.classMapper.getClass3”;//映射sql的标识字符串

19         //执行查询操作,将查询结果自动封装成Classes对象返回

20         Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录

21         //使用SqlSession执行完SQL之后需要关闭SqlSession

22         sqlSession.close();

23         //打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]

24         System.out.println(clazz);

25     }

26    

27     @Test

28     public void testGetClass4(){

29         SqlSession sqlSession = MyBatisUtil.getSqlSession();

30         /**

31          * 映射sql的标识字符串,

32          * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,

33          * getClass4是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL

34          */

35         String statement = “me.gacl.mapping.classMapper.getClass4”;//映射sql的标识字符串

36         //执行查询操作,将查询结果自动封装成Classes对象返回

37         Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录

38         //使用SqlSession执行完SQL之后需要关闭SqlSession

39         sqlSession.close();

40         //打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]

41         System.out.println(clazz);

42     }

43 }

复制代码

2.6、MyBatis一对多关联查询总结

  MyBatis中使用collection标签来解决一对多的关联查询,ofType属性指定集合中元素的对象类型。

分类: Mybatis

标签: MyBatis学习总结

好文要顶 关注我 收藏该文 

孤傲苍狼

关注 – 75

粉丝 – 2206

+加关注

11 0

(请您对文章做出评价)

  上一篇:MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突

  下一篇:MyBatis学习总结(六)——调用存储过程

posted on 2015-02-01 16:27 孤傲苍狼 阅读(21458) 评论(8) 编辑 收藏

评论

#1楼 2015-04-13 14:38 ChanShuYi 

写得很好,很详细,感谢分享。

支持(0)反对(0)

 

#2楼 2015-07-22 17:08 to_ny熊 

写的不错

支持(0)反对(0)

 

#3楼 2015-07-31 15:26 落叶秋思 

学习ing

支持(0)反对(0)

 

#4楼 2015-08-07 10:21 礼拜天001 

不错 ,正好用到

支持(0)反对(0)

 

#5楼 2015-08-17 14:39 jkguo 

Mark

支持(0)反对(0)

 

#6楼 2015-09-16 13:08 Laughing_Lz 

非常感谢!

支持(0)反对(0)

 

#7楼 2015-09-29 22:46 Ske.T 

如楼主所述:

1

2

3

4

5

6

7

8

<!–

    根据班级id查询班级信息(带老师的信息)

    ##1. 联表查询

    SELECT * FROM class c,teacher t WHERE c.teacher_id=t.t_id AND c.c_id=1;

    ##2. 执行两次查询

    SELECT * FROM class WHERE c_id=1;  //teacher_id=1

    SELECT * FROM teacher WHERE t_id=1;//使用上面得到的teacher_id

–>

这两个情况还是有区别的,

第1种在面对teacher_id == null,的情况下返回的class对象为空,因为sql返回的结果就是查询不到;

但是在第2种情况能够返回带teacher 为null的Class object,其他property还是都有结果的。

当然对应楼主这个范例,teacher 为空没什么意义,但别的情况下就得斟酌了。

支持(0)反对(0)

 

#8楼 2015-10-20 10:01 BraveSpringer 

我自己用mybatis的关联查询时,用的classDao接口来进行查询,获得的是null,但是用sqlsession的selectOne方法就可以获得数据,楼主能不能解答一下

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

dao.ClassDao

public interface ClassDao{

@Select(“select c.c_id,c.class_name,c.class_room,c.t_id,c.cap,t.teacher_name,t.subject from class c,teacher t where c.t_id=t.id and c.c_id=#{id}”)

    public Class getClassById(Integer id);

}

mapping.ClassMapper

<!– 根据id查询 –>

<select id=”getClassById” parameterType=”Integer” resultMap=”ClassResultMap”>

   select c.c_id,c.class_name,c.class_room,c.t_id,c.cap,t.teacher_name,t.subject from class c,teacher t where c.t_id=t.id and c.c_id=#{id}

</select>

 

<resultMap type=”model.Class” id=”ClassResultMap”>

<id property=”id” column=”c_id”/>  <!– id标签标注主属性 –>

<result property=”className” column=”class_name”/>

<result property=”classRoom” column=”class_room”/>

<result property=”cap” column=”cap”/>

 

<association property=”teacher” javaType=”model.Teacher”>

<id property=”id” column=”t_id”/>

<result property=”subject” column=”subject”></result>

<result property=”teacher_name” column=”teacher_name”></result>

</association>

</resultMap>

//测试

ClassDaoTest

@Test

    public void testGetClassById() {

    try{

        SqlSession sqlsession=MyBatisUtil.getSqlSession(true);

//基于注解的关联查询尚有问题    

               ClassDao cd=sqlsession.getMapper(ClassDao.class);

               Class cls=new Class();

//用session的selectOne方法可以获得数据                   //cls=sqlsession.selectOne(“mapping.ClassMapper.getClassById”,1);

     

      cls=cd.getClassById(3);

      System.out.println(“classdao”+cls.toString()+”; “+cls.getId());

    }

    catch(Exception ex){

        ex.printStackTrace();

    }

    //打印相关sql语句

C语言基础2)

1)C语言中的程序的执行过程

  1、C语言源文件(*.c)经过编译(Ctrl+F7)会生成*.obj的目标文件。此过程主要检查程序是否有错误, 如果有错误则会在下方弹出错误和警告

  2、目标文件必须经过链接(F7)生成*.exe文件才能直接在目标机器上运行

  3、运行执行文件(Ctrl+F5)

  注意:所有的快捷键指的是VC++6.0中的快捷键

2)良好的代码规范

  1、规范命名

     C语言的命名规范:

        1)C语言中严格区分大小写

        2)C语言命名需以下划线或者字母开头,不能以数字开头

        3)C语言中命名的长度一般只有前面8位有效

        4)不能是关键字和特定字

  2、C语言中标识符分类

        1)关键字

        2)特定字

        3)用户标识符

  3、代码规范

         1)空行。两个函数之间加空行,使程序更加清晰。前后联系较紧密的,则不应空行

         2)一行代码最好只做一件事

         3)在定义变量是就对变量初始化,可避免变量未初始化引发的问题

         4)”{“和”}”对齐,使代码简洁易读

         5)修饰符应紧靠变量,不容易使人产生误解

  4、合理的注释。C语言使用”/*注释内容*/”进行注释。

        1)简单的代码不用加注释

        2)注释应与源代码相近

        3)注释要适量,不可太多

        4)修改代码时,应修改注释,保持一致性

        5)注释应尽可能的准确、简洁

        6)对于结构化的程序,应在该结构的开头或者末尾加注释,便于理解和阅读。

3)输入输出

  1、输出

     printf(“输出的内容\n”);

     这是输出函数,会将指定的输出内容输出到控制台 \n是转义字符表示换行

   2、scanf()函数实现将键盘中输入的数据保存在变量中

   注意:scanf()函数中的”&”一定要有,否则输入的数据不能保存在变量中,程序运行会出现错误

#include <stdio.h>    
void main()  
{
	  int a;                           /*定义一个变量a*/
	  printf("input number:");         /*利用printf()函数输出提示语句*/
	  scanf("%d",&a);                  /*通过scanf()函数从键盘获取数据*/
	  printf("The number is:%d\n",a); /*通过printf()函数输出变量a的值*/
}


  • 大小: 7.3 KB
  • 大小: 24.2 KB

C语言基础1)

1、C语言的硬件条件

   c语言对硬件的要求不高,一般计算机都能满总。

   C语言的硬件条件:

   1)完整的指令系统

   2)有分别为运算和指针服务的16位寄存器

   3)堆栈指针和堆栈结构指针

   4)有连续的存取地址空间

2、C语言开发工具

   1)Visual c++:简称VC 一个功能强大的可视化集成开发工具

   2)Turbo C:快捷、方便、高效的开发工具

   3)C-Free:容易使用,比较适合初学者使用

   4)Win-TC

3、搭建开发环境(Visual c++ 6.0)

   1)创建及运行第一个程序

     安装vc++6.0百度上面有教程,很简单,基本就是下一步即可

1)打开vc++6.0

2)选择vc++6.0菜单栏中的文件(File)菜单,新建(New)选项

3)选择文件(File)选项卡,选中C++ source File 选项,并输入文件名、选择文件位置,点击确定



4)输入代码

5)对程序进行编译。选择菜单栏“Build”菜单中”Compile 文件名.c“选项,或者使用

Ctrl+F7快捷键可实现对程序的编译。如果程序无误,则会在下方弹出0个error和0个warning,表示程序编译没有任何错误和警告。

  

6)单击菜单栏“Build”菜单中“Build 文件名.exe”选项,或者按F7快捷键链接该程序。

 

7)单击菜单栏”Build“菜单中”Execute 文件名.exe“选项,或者按Ctrl+F5快捷键可运行该程序


  • 大小: 15.5 KB
  • 大小: 32.2 KB
  • 大小: 32.2 KB
  • 大小: 35.5 KB
  • 大小: 8 KB
  • 大小: 28 KB
  • 大小: 39.1 KB
  • 大小: 46.7 KB
  • 大小: 36.6 KB

iOS运行时机制

http://my.oschina.net/panyong/blog/298631    

转载的。

 

我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助!

首先,第一个问题, 
1》runtime实现的机制是什么,怎么用,一般用于干嘛? 
这个问题我就不跟大家绕弯子了,直接告诉大家, 
runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。 
在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者
比如说,下面一个创建对象的方法中, 
举例: 
OC : 
[[MJPerson alloc] init] 
runtime : 
objc_msgSend(objc_msgSend(“MJPerson” , “alloc”), “init”)

第二个问题 
runtime 用来干什么呢??用在那些地方呢?怎么用呢? 
runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)

  • 在程序运行过程中, 动态创建一个类(比如KVO的底层实现)

  • 在程序运行过程中, 动态地为某个类添加属性\方法, 修改属性值\方法

  • 遍历一个类的所有成员变量(属性)\所有方法 
    例如:我们需要对一个类的属性进行归档解档的时候属性特别的多,这时候,我们就会写很多对应的代码,但是如果使用了runtime就可以动态设置! 
    例如,PYPerson.h的文件如下所示

    import

@interface PYPerson : NSObject 
@property (nonatomic, assign) int age; 
@property (nonatomic, assign) int height; 
@property (nonatomic, copy) NSString *name; 
@property (nonatomic, assign) int age2; 
@property (nonatomic, assign) int height2; 
@property (nonatomic, assign) int age3; 
@property (nonatomic, assign) int height3; 
@property (nonatomic, assign) int age4; 
@property (nonatomic, assign) int height4;

@end

而PYPerson.m实现文件的内容如下

<!-- lang: cpp -->
#import "PYPerson.h"

import

@implementation PYPerson

  • (void)encodeWithCoder:(NSCoder )encoder 

    unsigned int count = 0; 
    Ivar 
    ivars = class_copyIvarList([PYPerson class], &count);

    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];
    [encoder encodeObject:value forKey:key];
    

    }

    free(ivars); 
    }

  • (id)initWithCoder:(NSCoder *)decoder 

    if (self = [super init]) {

    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([PYPerson class], &count);
    
    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 = [decoder decodeObjectForKey:key];
    
        // 设置到成员变量身上
        [self setValue:value forKey:key];
    }
    
    free(ivars);
    


    return self; 
    }

@end

这样我们可以看到归档和解档的案例其实是runtime写下的

学习,runtime机制首先要了解下面几个问题 
1相关的头文件和函数 
1> 头文件


  • 利用头文件,我们可以查看到runtime中的各个方法!

2> 相关应用

  • NSCoding(归档和解档, 利用runtime遍历模型对象的所有属性)
  • 字典 –> 模型 (利用runtime遍历模型对象的所有属性, 根据属性名从字典中取出对应的值, 设置到模型的属性上)
  • KVO(利用runtime动态产生一个类)
  • 用于封装框架(想怎么改就怎么改) 
    这就是我们runtime机制的只要运用方向

3> 相关函数

  • objc_msgSend : 给对象发送消息
  • class_copyMethodList : 遍历某个类所有的方法
  • class_copyIvarList : 遍历某个类所有的成员变量
  • class_….. 
    这是我们学习runtime必须知道的函数!

4.必备常识 
1> Ivar : 成员变量 
2> Method : 成员方法 
从上面例子中我们看到我们定义的成员变量,如果要是动态创建方法,可以使用Method,

 

也许,看到这里,你是否对runtime有了更深入的了解呢?在这里,希望我们大家相互交流!有什么错误之处,还请指正