进程间通信方式:

                  

同主机进程间数据交换机制: pipe(无名管道) / fifo(有名管道)/ message queue(消息队列)和共享内存。

必备基础: fork() 创建一个与之前完全一样的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。

一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都

复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

  vfork : 与fork用法相同,但是他和父进程共享同样的数据存储,因此无需完全复制父进程的地址空间。

// fork() study example 1
#include <unistd.h>
#include <stdio.h>
int main ()
{
pid_t fpid; //fpid表示fork函数返回的值
int count=; // fork 会将这个变量存在两个不同的内存中,所以两次count的值都是 1 ,而不是 1,2 。
fpid=fork();
if (fpid < )
printf("error in fork!");
else if (fpid == ) {
printf("i am the child process, my process id is %d、n",getpid());
printf("我是爹的儿子\n");//对某些人来说中文看着更直白。
count++;
}
else {
printf("i am the parent process, my process id is %d\n",getpid());
printf("我是孩子他爹\n");
count++;
}
printf("统计结果是: %d\n",count);
return ;
}

运行结果:

在for之前只有一个进程执行代码,但是在fork之后就会再创建一个进程去同时执行这段代码。

程序通过fork的返回值fpid判断是子进程还是父进程,还是创建进程失败。

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
    1)在父进程中,fork返回新创建子进程的进程ID;  //相当于父进程指向自己的子进程,而子进程没有孩子进程可以指向。
    2)在子进程中,fork返回0;
    3)如果出现错误,fork返回一个负值;

fork出错可能有两种原因:
    1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
    2)系统内存不足,这时errno的值被设置为ENOMEM。

#include <unistd.h>
#include <stdio.h>
int main(void)
{
int i=;
printf("i son/pa ppid pid fpid\n");
//ppid指当前进程的父进程pid
//pid指当前进程的pid,
//fpid指fork返回给当前进程的值
for(i=;i<;i++){
pid_t fpid=fork();
if(fpid==)
printf("%d child %4d %4d %4d\n",i,getppid(),getpid(),fpid);
else
printf("%d parent %4d %4d %4d\n",i,getppid(),getpid(),fpid);
}
return ;
}

1. 在执行第一个循环时:

  pid=5944 的进程 ,创建了 一个子进程 5945

2. 第二次循环中:

  5944 的进程穿件了  pid=5946的子进程

  5945 的进程作为父进程创建了 pid=5947 的子进程

p5947的父进程 应该是 5945 ,但是 这时 5945进程肯能已经死亡。 (具体原因自己还没有弄明白,如需深入学习可以见参考资料)

                                                            进程间通信: 管道及无名管道

竞争条件:两个或者多个进程读写某些共享数据,而最后的结果取决于进程的运行的精确时序,称为竞争条件。(把条件理解成情况,竞争情况,貌似更加容易理解一些=。=)

互斥:互斥是一种手段,它使共享数据的进程无法同时对其共享的数据进行处理

临界区:即访问共享内存的程序片。也就是说,通过合理的安排,使得两个进程不可能同时处在临界区中,就能避免竞争条件。满足一下四个条件

  a/ 任何两个进程不能同时处于临界区

  b/ 不应对cpu速度和数量做任何假设

  c/ 临界区外运行的进程不得阻塞其他进程

  d/ 不得使进程无限期的等待进入临界区

忙等待互斥的几种实现方法

  a/ 屏蔽中断:屏蔽中断之后cpu不会被切换到其他进程,他就可以检查和修改共享内存,而不必担心其他进程的进入。

  缺点:多核的系统无效(其他进程任然可以占用其他的CPU继续访问公共内存)

用户程序来控制中断会很危险(使想一下,一个进程屏蔽中断后不再打开中断,那你的系统就GG了)

     结论:屏蔽中断对系统本身是一项很有用的技术,但对用户进程不是一种合适的通用互斥机制。

  b/ 锁变量:屏蔽中断的软件实现机制。

假定一个共享(锁)变量,初值为0,代表临界区内无进程,进程进入临界区后将其改变为1,代表临界区内有进程;倘若进程在进入临界区之前,

锁变量值为1,该进程将等待其值变为0。未能实现的原因:与假脱机目录的疏漏一样,如果一个进程进入临界区,但是在它把锁变量置1之前被中断,另一

个进程进入临界区后将0置1,这样, 当前一个进程再次运行时它也将锁变量置1,这样临界区内依然存在两 个进程。    

  

  c/严格轮换原理:共享turn变量,用来记录轮到那个进程进入临界区。

    当turn=0时,只有进程0能进入临界区,进程0在离开临界区前将turn置1,从而标志,轮到进程1进入临界区。

缺点:严格地轮换,可能导致临界区外的进程阻塞需要进入临界区的进程(例如:当turn=0时,意味着只有进程0能进入临界区,这时如果进程

0还要100年才会退出临界区,而进程1需要马上进入,那进程1还要白白等100 年.)

总结:当一个进程比另一个进程慢了许多的情况下,不宜用这种方式。  

  d/ Peterson解法

这是Peterson本人发明的一种简单的互斥算法

#define FALSE 0
#define TRUE 0
#define N 2
int turn ; //当前轮转到那个进程
int intre[N]; //初始化置为false,即没有在临界区等待读写共享数据的
void enter_region(int process){ int other ; other = -process; intre[process] = TRUE; turn = process; while(turn==process&&intre[other]==TRUE)
      ; //一直循环,直到other进程退出临界区
} void leave_process(int process){ intre[process] = FALSE; }

我们分情况跑一遍程序:

a、进程0通过调用enter_region()进入临界区,此时1也想调用enter_region()  来进入临界区,但interested[0]为TRUE,因此被while循环挂起,当进程0出临

界区时调用leave_region(),将interested[0]设为FALSE,进程1即可及时进入临界 区。

b、当进程0在调用enter_region()过程的任意时刻被中断,进程1进入临界区 后进程0再次进行时,依然会被挂起。(实际上while循环体中的两条判断句就

保证了,当一个进程在临界区中时,另一个想进入临界区的进程必然会被挂起)。

信号量

  •信号量对应于某一种资源,取一个非负的整型值
  •信号量值指的是当前可用的该资源的数量,若它等于0则意味着目前没有可用的资源
在该信号量下等待资源的进程等待队列
对信号量进行的两个原子操作(PV操作)
  •P操作
  •V操作
 

编程步骤:

  创建信号量或获得在系统已存在的信号量
    •调用semget()函数
    •不同进程使用同一个信号量键值来获得同一个信号量
  初始化信号量
    •使用semctl()函数的SETVAL操作
    •当使用二维信号量时,通常将信号量初始化为1
  进行信号量的PV操作
    •调用semop()函数
    •实现进程之间的同步和互斥的核心部分
  如果不需要信号量,则从系统中删除它
    •使用semclt()函数的IPC_RMID操作
    •在程序中不应该出现对已被删除的信号量的操作
一、无名管道(pipe)
 
1.1管道的介绍
 
  A.管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
 
  B.只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程,或者共同祖先的进程);
 
  C.单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
 
  D.数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
 
1.2管道的创建 (包含在头文件:  unistd.h)
  管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道,一般文件I/O的函数都可以用来操作管道(lseek除外)。单独创建一个无名管道,并没有实际的意义。
a/创建一个单通道的管道
  (1)由pipe()创建管道
  (2)fork创建一个子进程
  (3)父进程关闭这个通道的读出端 f[0]
    (4) 子进程关闭同一管道的写入端 f[1]
这就在父子进程之间提供了一个单向数据流。 如下图所示

b/ 创建一个双通道的管道通信

  (1)由pipe()创建管道
  (2)fork创建一个子进程
  (3)父进程关闭通道1的读出端 fd[0]
    (4) 父进程关闭通道2的写入端 fd[1]
  (5) 子进程关闭通道1的写入端fd[1]
    (6)  子进程关闭通道2的写入端fd[0]
这就在父子进程之间提供了一个双向数据流。 如下图所示
                                                              

插播一个知识点:

读函数read

ssize_t read(int fd,void *buf,size_t nbyte) 
read函数是负责从fd中读取内容.成功时,read返回实际所读的字节数,如果返回的值是0,表示已经读到文件的结束了。小于0表示出现了错误.如果错误为EINTR说明读是由中断引起的, 如果是ECONNREST表示网络连接出了问题。
 
写函数write 
ssize_t write(int fd,const void *buf,size_t nbytes) 
write函数
将buf中的n bytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有俩种可能.  
  1)write的返回值大于0,表示写了部分或者是全部的数据.  
  2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.  如果错误为EINTR表示在写的时候出现了中断错误.  如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接).

例子:父进程读取文件的内容,写到无名管道,子进程从管道中读取内容写到另一个文件。
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> #define MAX 100 int child_work(int pfd,char *fname)
{
int n,fd;
char buf[MAX]; if((fd = open(fname,O_WRONLY | O_CREAT | O_TRUNC,)) < )
{
fprintf(stderr,"Fail to open %s : %s.\n",fname,strerror(errno));
return -;
} while( n = read(pfd,buf,sizeof(buf)) )
{
write(fd,buf,n);
} close(pfd); return ;
} int father_work(int pfd,char *fname)
{
int fd,n;
char buf[MAX]; if((fd = open(fname,O_RDONLY)) < )
{
fprintf(stderr,"Fail to open %s : %s.\n",fname,strerror(errno));
return -;
} while(n = read(fd,buf,sizeof(buf)))
{
write(pfd,buf,n);
} close(pfd); return ;
} int main(int argc,char *argv[])
{
int pid;
int fd[]; if(argc < )
{
fprintf(stderr,"usage %s argv[1] argv[2].\n",argv[]);
exit(EXIT_FAILURE);
} if(pipe(fd) < )
{
perror("Fail to pipe");
exit(EXIT_FAILURE);
} if((pid = fork()) < )
{
perror("Fail to fork");
exit(EXIT_FAILURE); }else if(pid == ){ close(fd[]);
child_work(fd[],argv[]); }else{ close(fd[]);
father_work(fd[],argv[]);
wait(NULL);
} exit(EXIT_SUCCESS);
}

子进程: 读取 管道中的数据进入buf,然后将buf中的数据写入到 file2中去

父进程: 从file1中读取数据,然后写进管道中给子进程去读

 二、有名管道
  shell中创建有名管道:   mknod PIPETEST p 
  1.1简介: 无名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO)。 FIFO不同于无名管道之处在于它提供了一个路径名与之关联,
以FIFO的文件形式存在于文件系统中,这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信,因此,通过FIFO不相关
的进程也能交换数据。值的注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持
诸如lseek()等文件定位操作。
 
注意:有名管道的名字存在于文件系统中,内容存放在内存中
  1.2有名管道的创建 

int mkfifo(const char * path_name ,mode_t mode);

该函数的第一个参数是一个普通的路劲名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode参数相同。如果mkfifo的一个参数是一个已经存在路劲名时,会返回EEXIST错误,

所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。

  1.3有名管道的打开规则
 
  有名管道比无名管道多了一个打开操作:open
 
  FIFO的打开规则:
 
  如果当前打开操作时为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功
返回(当前打开操作没有设置阻塞标志)。如果当前打开操作时为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该
FIFO(当前打开操作设置了阻塞标志);或者,返回ENIO错误(当期打开操作没有设置阻塞标志)。

创建管道成功后,可使用open()、read()和write()等函数。

  为读而打开的管道可在open()中设置O_RDONLY
  为写而打开的管道可在open()中设置O_WRONLY
open for write 
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main(int argc,char *argv[])
{
int fd; if(argc < )
{
fprintf(stderr,"usage : %s argv[1].\n",argv[]);
exit(EXIT_FAILURE);
} if(mkfifo(argv[],) < && errno != EEXIST)
{
fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} if((fd = open(argv[],O_WRONLY)) < )
{
fprintf(stderr,"Fail to open %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} printf("open for write success.\n"); return ;
}

open for read

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main(int argc,char *argv[])
{
int fd; if(argc < )
{
fprintf(stderr,"usage : %s argv[1].\n",argv[]);
exit(EXIT_FAILURE);
} if(mkfifo(argv[],) < && errno != EEXIST)
{
fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} if((fd = open(argv[],O_RDONLY)) < )
{
fprintf(stderr,"Fail to open %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} printf("open for read success.\n"); return ;
}
  1.4有名管道的读写规则
 
A.从FIFO中读取数据
 
  约定:如果一个进程为了从FIFO中读取数据而以阻塞的方式打开FIFO, 则称内核为该进程的读操作设置了阻塞标志
 
  <1>如果有进程为写而打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说返回-1,当前errno值为EAGAIN,提醒以后再试。
 
  <2>对于设置阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其他进程正在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量。
 
  <3>如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞
 
  <4>如果写端关闭,管道中有数据读取管道中的数据,如果管道中没有数据读端将不会继续阻塞,此时返回0。
 
  注意:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。
 
B.向FIFO中写入数据
 
  约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作设置了阻塞标志。
 
  对于设置了阻塞标志的写操作:
 
  <1>当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳写入的字节数时,才开始进行一次性写操作。
 
  <2>当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
 
对于没有设置阻塞标志的写操作:
 
  <1>当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
 
  <2>当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写。
 
注意:只有读端存在,写端才有意义。如果读端不在,写端向FIFO写数据,内核将向对应的进程发送SIGPIPE信号(默认终止进程)
write to fifo 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #define MAX 655360 int main(int argc,char *argv[])
{
int n,fd,i;
char buf[MAX]="i am the buf in write";
// string temp ;
if(argc < )
{
fprintf(stderr,"usage : %s argv[1].\n",argv[]);
exit(EXIT_FAILURE);
} if(mkfifo(argv[],) < && errno != EEXIST)
{
fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} if((fd = open(argv[],O_WRONLY )) < )
{
fprintf(stderr,"Fail to open %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} printf("open for write success.\n"); while()
{
printf(">");
int i=;
while((buf[i]=getchar())!='\n')
i++;
// scanf("%s",&temp);
// for(i=0;i<sizeof(temp);i++)
// buf[i]=temp[i];
n = write(fd,buf,i);
printf("write %d bytes.\n",n);
} exit(EXIT_SUCCESS);
}

read from fifo

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #define MAX 20 int main(int argc,char *argv[])
{
int fd,n,i;
char buf[MAX]; if(argc < )
{
fprintf(stderr,"usage : %s argv[1].\n",argv[]);
exit(EXIT_FAILURE);
} if(mkfifo(argv[],) < && errno != EEXIST)
{
fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} if((fd = open(argv[],O_RDONLY )) < )
{
fprintf(stderr,"Fail to open %s : %s.\n",argv[],strerror(errno));
exit(EXIT_FAILURE);
} printf("open for read success.\n"); while()
{
printf(">");
scanf("%d",&n); n = read(fd,buf,n); printf("Read %d bytes.\n",n);
for(i=;i<;i++)
printf("%c",buf[i]);
printf("\n"); } exit(EXIT_SUCCESS);
}

将write to fifo 部分中,输入一个字符串进入到buf中,再将buf中这个字符串写入到 fifo中,另一个进程再从fifo中读出数据

可以将这两个程序运行,然后输入read和write   FIFO大小就可以看到效果。

参考资料:

http://blog.csdn.net/w616589292/article/details/50957456

http://www.cnblogs.com/bastard/archive/2012/08/31/2664896.html#3311781

linux之间进程通信的更多相关文章

  1. Linux下进程通信的八种方法

    Linux下进程通信的八种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号量 ...

  2. Linux之进程通信20160720

    好久没更新了,今天主要说一下Linux的进程通信,后续Linux方面的更新应该会变缓,因为最近在看Java和安卓方面的知识,后续会根据学习成果不断分享更新Java和安卓的方面的知识~ Linux进程通 ...

  3. Linux下进程通信之管道

    每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把 ...

  4. Linux编程---进程通信

    Linux的通信方式主要有分类有以下几种: -匿名管道和FIFO有名管道 -消息队列,信号量和共享存储 -套接字 对于套接字的进程通信,我就留在套接字的文章中再写了. 一.管道 管道是最古老的进程通信 ...

  5. Linux:进程通信之消息队列Message实例

    /*send.c*/ /*send.c*/ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h&g ...

  6. [置顶] 简单解析linux下进程通信方法

    linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...

  7. ZeroMQ实例-使用ZeroMQ进行windows与linux之间的通信

    1.本文包括 1)在windows下使用ZMQ 2)在windows环境下与Linux环境下进行网络通信 2.在Linux下使用ZMQ 之前写过一篇如何在Linux环境下使用ZMQ的文章 <Ze ...

  8. 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇

    前言 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Andro ...

  9. 进程以及进程通信(IPC)类型

    这里用我有限的知识来解释同时参考了一些其他博主的子类,希望能给与一部分入门的朋友一个清晰的理解,有问题之处还请指出 首先简单谈一下什么是进程? 答:进程是装入内存运行的程序段,是许多的系统对象拥有权的 ...

随机推荐

  1. NYOJ 42 一笔画问题

    一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...

  2. TSQL生成Combguid

    Nhibernate实现combguid /// <summary> /// Generate a new <see cref="Guid"/> using ...

  3. SharePoint 2013 自定义模板页后在列表里修改不了视图

    前言 最近系统从2010升级至2013,有自定义模板页.突然发现在列表中切换不了视图,让我很费解. 我尝试过以下解决方案: 去掉自定义css 去掉自定义js 禁用所有自定义功能 结果都没有效还是一样的 ...

  4. Winform使用外部浏览器解决webbrowser问题

    对于还是一个菜鸟的我,在最近自己接手了个项目,搞的自己也是醉了,身边也有没大神的现场指导,只能靠度娘和谷歌的大力帮助,要不然这么个小项目可定现在还交不了,不过在这过程种也确确实实学到了不少东西,我先说 ...

  5. sharepoint 中waiting screen dialog的使用方法(JSOM)

    sharepoint中有一个种wait screen的弹出框,其实就是一直转圈,告诉你等待一会儿时间.用法如下: 弹出: var watiDialog = SP.UI.ModalDialog.show ...

  6. JAVA-使用commos-fileupload实现文件上传与下载

    一文件的上传 实体类 在CODE上查看代码片派生到我的代码片 import java.sql.Timestamp; /** * * @Decription 文件上传实体类 * @author 刘楠 * ...

  7. 再也不用管UIImagePicker的代理了

    EasyImagePicker 闲暇之余对UIImagePicker做了封装,将代理调用的方式封装成block回调的方式,这样一行代码就能够搞定UIImagePicker的使用,包括选择图片,取消选择 ...

  8. iOS之多控制器管理--项目中的常见文件

    项目中的常见文件 内容大纲: 1.LaunchScreen 2.info.plist文件 3.pch文件 1.LaunchScreen xcode5和xcode6区别 1.xcode6没有Framew ...

  9. 《极客学院 --NSAttributedString 使用详解-4-UITextKit 简介》学习笔记(待处理)

    如果要在富文本中添加图片的话,用UITextKit才能实现. 什么是UITextKit:它就是处理富文本的框架. 什么时候使用UITextKit:比如要实现图文混搭的节目. 在gitHub中 http ...

  10. Effective Java 14 In public classes, use accessor methods, not public fields

    Principle To offer the benefits of encapsulation you should always expose private field with public ...