一、程序运行的内存详解
首先,我们需要明白理解几个东西,程序都是在内存中跑的,因此我们必须弄清楚内存跟程序有什么关联?
内存中分基本分为了3个区,分别是栈区、堆区、常量区。
这个我们首先理解几句简单的代码
第一段代码;
Int i = 0; 在内容中存储一个变量名叫做i的,内容为0 的内容,在一个内存中
NSLog(@”%p”,i);这个输出的内存块的内容,然而内存块的内容又不是指针类型,这报错
NSLog(@”%p”,&i);&i指的就是指针类型,看了下面的,这里我们就可以理解了。
什么样的才是指针类型?
我们知道整型、浮点型是什么样子的,他们指的是一种长得特定“相貌”容器,什么样的容器装什么样的内容,类似我们可以知道,指针类型也是长着特定“相貌”的容器,装载的东西是长着跟内容地址一样的内容。
第二段代码:
Person *p = [Person alloc]init];
这里在栈里面存储着一个p指针变量,里面放的内容是堆中的某块内存的内存地址
NSLog(@“%p”,&p);//这里我们输出的是p在栈中所占的那块内存的地址
NSLog(@“%p”,p); //这个是P里面的内容,类似i= 0 一样
NSLog(@”%@”,p); //这个是p这个指针类型的内容,所指向堆中的那块内存的地址
能搞起这个内存就没有问题了,还有常量区,也叫静态区,全局区,放的都是些常量,静态变量,着就没有什么理解的了。
接下来我们讲解另一个内容:多线程
二、创建线程的三种方式
我们前面既然已经知道了线程的一些概念,所以接下来我们做的就是创建出线程以供我们使用
第一种方式:实例化方法
//这些方法中的object就是我们绑定的方法里面的参数
NSThread *thread = [[NSTread alloc]initWithTarget:self selector
selector(run
object:@”hahha”];//实例化线程
[Thread start];//启动线程,当然还可以设置线程的名字thread setname
第二种方式:类方法
[NSThread datachNewThreadSelector:@selector(run:) totarget:self withObject:nil]
第三种方式:foundation框架里面定义的
//NSobject 定义的方法,隐式的多线程调用
[self performSelectorInbackground:@selector(run:) withObject:nil];
三、线程状态
根据我们以前学习过的内容,知道线程的状态有阻塞、休眠、kill等状态,这里我们使用的是睡眠、kill的状态,直接调用相应的方法即可,这里我们还需要注意的就是,UI的线程是第一的线程,线程是分1、2、3、4、......等等,表示说明的是第几线程。
[NSthread sleepForTimeInterval]------sleepForTimeInterval
[NSthread exit]------------exit
四、线程间的通信
线程间的通信,这里我们就学习了一个,子线程把一个任务完成,然后让主线程调用方法进行刷新:
[Self performSelectOnMainThread:@selector(setImage:) withObaject:image waitUnitlDone:NO]
五、线程间的资源争夺
要明白这个,首先我们需要知道线程的工作方式,CPU对线程的执行,是以一种非常快的方式切换的,在CPU那里线程运行其实是单线的,因为CPU 速度太快了,所以让我们决定是多线程,对于多核的处理器那就是多了几个CPU而已。
理解了这个,就知道线程间的资源争夺了,这里牵涉到我们在操作系统中“互斥锁”的概念,以前学过,这里就不累赘了,还有,苹果希望我们还是少用互斥锁,互斥锁的关键词@synchonnized。
这里又牵涉到一个知识点,那就是我们添加属性的时候nonatomic,atomic线程保护,在atomic的属性,读取的时候不加锁,但是写的时候加锁。
六、自动释放池与线程的关系
在主线程中有自动释放池进行控制,但是在子线程中编译器是不会自动给它添加自动释放池的,因此我们在写子线程的时候一定记得添加自动释放池进行内存的管理。
for(int i ;i < 10;i++)
{
NSstring * str = @”helloworld”;
Str = [str uppercaseString];
Str = [NSString stringwithformat:@”%@-123”,str];
}
提问:以上代码有什么问题?
解释:以上的代码会浪费大量的内存资源,i的值越大浪费的就越多,这里首先需x先明白自动释放池,在自动释放池中,当出了作用域后,里面所有的对象都会添加到最近的autorelease中,autorelease会延迟释放,这里我们又可以联系到我们在UI中写的那些strong的类型,如果我们定义了strong类型,那么程序中就会有一个强指针指向这个对象,当出了作用域,autorelease就释放了它,因为有一个强指针正在“牵”着它,如果是弱引用,那么就在劫难逃了。弄清了autorelease后,那我们这里就可以理解了,以上for中的每一条语句都会产生一个新的对象,而且都是没有释放的垃圾对象,这将导致内存泄露。
如果数据较大,那我们采取的方式就把for放在一个自动释放池的一个作用域中,如果非常非常的大,那么我们就把自动释放池放在for的里面。
七、block经典面试题(前提是先知道什么是block才看的懂这个,什么是block我这就不累赘了,前面的知识已经讲了很多了),这里给一个小技巧,在xcode区域桥inlineblock就会自动出现block。
第一种:
X = 10;
Void(^myblock)() = ^{
NSLog(@”%d”,x);
}
Int x = 20 ;
Myblock();
这个的结果输出的是10,为什么X已经被赋值为20 了,输出还是为10 ,那是因为block调用block函数代码的时候,就只看到前面的X,并没有及时更新X的内容。这里还要附加一点,那就是这个block里面的变量参数都是外部的副本,都是有自己的内存空间的,相当于copy。
第二段:
X = 10;
Void(^myblock)() = ^{
X = 50;
NSLog(@”%d”,x);
}
Int x = 20 ;
Myblock();
这个不允许的,在block内部是不允许对外部的变量进行修改的,就算是读取也无法读取到最新的内容,只能读取到之前的内容,这有时可以解决一些棘手的问题。
想到block修改外部,已经自己及时更新外部的消息,我们这里就需要定义个成block形式的变量,这里block对这个变量才有所有的权限
第三种:
__X = 10;(重点注意这里,前面加了__,就是表示这个跟block有关的,block对这个具有所有权)
Void(^myblock)() = ^{
NSLog(@”%d”,x);
}
Int x = 20 ;
Myblock();
这个时候,结果就是二十了,而且可以直接在里面写代码进行修改这个变量,这时block就会对这个block有绝对的所有权。
还有第四种:
NSString *str = [NSString stringwithstring:@”hello”];
Void(^myblock)() = ^{
[Str setstring:@”world”];
NSLog(@”%@”,str);
}