我们平时所写的代码,也就是.h .m文件,编译完成后就会变成01010101...的机器码。这些代码最终是会载
进内存的,放在内存的代码段。所以代码端就是存放010101...那些代码的,也就是编译后的代码
函数里面的局部变量都是放在栈里面的,0x7 是十六进制位,转为二进制位是0b0111
变量e,f,g都是放在栈区的(不管有没有初始化),而且e的地址值大于f的地址值,f的地址值大于g的地址值,不断去用栈空间的话,内存地址值是越来越小的---a,c,b,d放在数据段
内存分布示意图
int a = 10;
int b;
int main(int argc, char * argv[]) {
@autoreleasepool {
static int c = 20;
static int d;
int e;
int f = 20;
NSString *str = @"123";
NSObject *obj = [[NSObject alloc] init];
NSLog(@"\n&a=%p\n&b=%p\n&c=%p\n&d=%p\n&e=%p\n&f=%p\nstr=%p\nobj=%p\n",
&a, &b, &c, &d, &e, &f, str, obj);
NSLog(@"---指针地址str=%p\n指针地址obj=%p",&str,&obj);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
--------由打印可知,obj指向的NSObject创建的对象在堆区,而obj指针本身是在栈区
字符串常量
str=0x108cbe090
已初始化的全局变量、静态变量
&c= 0x108cbeea0
= 0x108cbee98
&a= 0x108cbee9
未初始化的全局变量、静态变量
&d= 0x108cbef68
&b= 0x108cbef6c
堆
obj=0x6000038dc0c0
栈
指针地址app=0x7ffee3586c40
指针地址obj=0x7ffee6f43c48
指针地址str=0x7ffee6f43c50
&f=0x7ffee6f43c58
&e=0x7ffee6f43c5c
有标记的指针。能节省很多的内存空间,这都是编译器帮我们做的
右边的27就是标记tag
如果存的值太大,就会变成普通的oc对象的存储方式,也就是说如果Tagged Pointer 8个字节存不下的话,就会变成Tagged Pointer之前的存储方式,也就是number3存储的是nsnumber对象在堆空间的地址值。只要是堆空间对象的地址值,最低有效位肯定是0
总结
self.name = [NSString stringWithFormat:@"abcdefghijk"];的本质是调用set方法,
也就是[self setName:(NSString *)]; assign修饰属性,set方法就是直接赋值
copy的修饰的字符串的set方法在arc环境下是这样的。但是arc的本质是转成mrc
所以copy修饰的字符串的set方法真正底层长这样子。如果上面的修饰词copy变成strong,下面的[name copy]就变成[name retain],但前面的[_name release] 是不变的。也就是不管是copy修饰还是retain修饰,肯定要先释放掉旧的成员变量
运行程序会crash,因为_name可能同时被多条线程访问,也就是会有多条线程同时调用[_name release]。假如线程1调用了[_name release],把_name释放了,线程2再来调用[_name release],就会造成坏内存访问
str1指向的是堆空间的一个字符串对象,str2是一个Tagged Pointer指针
这段代码不会crash,原因右边是一个Tagged Pointer指针,不会调用所谓的set方法,直接是指针赋值。相当于self.name = 0xa00000636261,直接把地址值赋值给self.name了
类型差异
判断是否是Tagged Pointer
此时a就是最高有效位
这样都不够64位,所以在前面加0填充
网友评论