1-了解Objective - C语言的起源
先简单的说一下:消息结构和函数调用这两者之间的区别
同样是调用一个对象的方法,
//Objective-C
Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];
//C++
Object *obj = new Object;
obj->perform(parameter1, parameter2);
他们两者之间不单只存在语法上的区别,关键区别在编译器在执行这段代码的时候的一个机制是怎么样的:
- 消息结构的语言:
- 运行时由运行环境决定所应执行的代码
- 不管是否多态,总是在运行时才去查询要执行的方法(在objective-c编译器是怎么知道该执行哪个实现的呢,可以去了解一下Runtime)
- 使用函数调用的语言:
- 由编译器决定。
- 当然如果调用的函数是多态的话,编译器会在运行的时候按照“虚方法表”查出到底执行哪个实现,和消息结构的语言不同
上面简单的讲述了消息结构的语言和函数调用的语言的区别,Objective-C是由Smalltalk演化而来的消息型语言。
在使用Objective-C编写的代码到实际呈现出来的效果,中间其实是由“运行期组件”来链接的。本章中有一句话“只更新运行期组件,即可提升应用程序性能,而那种许多工作都在“编译期”完成的语言,想获得类似的性能提升,需要重新编译应用程序代码”,我们编写的项目都需要经过苹果商店审核发布,每次提交版本就是一个等待,那么我们是否可以通过运用强大的runtime通过后台给项目来个及时更新呢?当然具体实现起来并非三言两语便能做出来的,呵呵。
总所周知,Objective-C是可以与C语言混编的,为什么呢,因为Objective-C是C的“超集”,什么叫“超集”,意思就是C语言中的所有功能在编写Objective-C代码时依然是管用的,当然有的时候需要在项目中适当的添加系统库引入头文件等等。
所以说想把Objective-C学好,用得6的话,C语言是必须得掌握的,因为Objective-c的系统库中,苹果给我们提供的cocoa foundation虽然封装得很好,但是有时候还是不能够满足我们的日常开发的,所以我们需要了解到更底层的foundation,然而正正它们就是由C语言编写的,对于底层的foundation,其它的不说,起码语法要能看懂吧,更深入一点的,内存管理要懂吧,Objective-C中的由C语言提供的接口生成的对象一般都不在ARC范围内,很多时候都是需要自己去管理对象的内存,所以说这个C还是得好好学一下的。再者,Objective-c的底层运行机制,我们用Objective-C编写的代码经编译器编译之后都是先由刚刚说的运行期组件先转换为C语言,再往下走的,并且Objective-c的对象,类,方法调用,函数体等等,都是由C语言实现的,所以想深入了解Objective-C的话,好好学一下C才行,有空我也得连早餐都不吃就来多啃啃那本红书皮的C什么什么才行。
接下来简单的说一下堆,栈,对象,变量,看代码:
NSString *someString = @"Jk_Chan";
NSString *anotherString = someString;
这两行代码,只有一个实例对象,那就是通过@""生成的NSString实例,有someString,anoterString这两个变量,可以看出什么是对象和变量了吧。
然后,对象在生成的时候会从内存中申请空间,所申请的内存空间便是在堆上的,而变量,小时候老师说变量是用来存储数据的,确实,既然要存储数据,那么也得有一块内存才行,然后变量所在的内存和对象所在的是有区别的,对象所在的刚刚说了是在堆上,而变量的就是在栈上。那么堆和栈上的内存有什么区别呢?
堆中的内存需要直接管理,而栈的内存会在栈帧弹出时自动清理,什么叫栈帧弹出?应该和变量的作用域挂钩吧,什么叫作用域?叫老师还学费,哈哈,开个玩笑。
既然堆中的内存也就是对象需要直接管理,但是好像我们在平时编写代码的时候也没有刻意去管理啊,好吧,其实是有刻意去管理的,如果你了解变量所有权修饰符的话。只是我们管理的内存不是直接通过分配,释放去管理对象,而是遵循苹果的一套内存管理架构,名叫“引用计数”,也就是ARC。
分配内存又应该怎么分配,分配多少呢?简单的说堆中的内存大小会根据对象所属类内部变量等等所占字节数去分配,栈中变量的内存像Objective-C中的变量实际是一个指针,对应类型的地址占多少字节那么就分配多少字节。
看回到刚刚那两行代码,来做一下章节外的延伸:
NSString *someString = @"Jk_Chan1";
NSString *anotherString = someString;
由于两个变量指向同一个对象,也就是指向同一块内存,如果,如果可以通过调用变量中的一些方法而改变所指的对象内存数据,那么另一个变量所指的数据当然也会不一样了。但是,比如我啊,我以前就总是觉得,比如:
anotherString = @"Jk_Chan2";
好了,anotherString所指的对象改变了,那么为什么输出someString的时候还是JK_Chan1而不是改了的JK_Chan2呢,之前明明它们这两个变量所指的都是同一个对象地址呀?
其实是我傻XXX了,怎么说呢?
anotherString = @"JK_Chan2";
这行代码应该怎么用普通话说出来,就是生成一个值为Chan_Jk的NSString对象赋给anotherString变量,也就是 是这个变量的值改变了,它所指的内存地址不同了,它变心了,它爱上另外一个对象了,也就是说旧的那个对象还是旧的那个对象,它所处的那一块内存数据还是没变到,原来是飞机场的还是飞机场,只是那个变量TMD变心了,指向其它对象去了。所以someString 所指的内存数据还是一样的。
然后这里还有一点,就是引用计数的问题。
原来JK_Chan1明明是有两个人喜欢自己的,someString,anotherString嘛,心里面都是向着自己的,可是好了,有一个变心了,指向2了,所以就剩一个人喜欢自己了,没办法啊,╮(╯▽╰)╭,所以这里Jk_Chan1的持有者只有someStirng,引用计数为1了。要是someStirng都变心了,好吧,没人爱自己了,死了算吧,这时候Jk_Chan1所占的内存便从堆中释放掉了。
然后再来变异一下:
NSMutableString *someString = [@"123" mutableCopy];
NSMutableString *anoterString = someString;
NSLog(@"someString:%@,anoterString:%@",someString, anoterString);
[anoterString appendString:@"456"];
NSLog(@"someString:%@,anoterString:%@",someString, anoterString);
输出的结果是什么呢?
2016-03-14 00:23:47.830 Nice_Project[1541:122585] someString:123,anoterString:123
2016-03-14 00:23:47.831 Nice_Project[1541:122585] someString:123456,anoterString:123456
为什么会这样呢?
因为anoterString 虽然嫌弃这个对象是飞机场,但还是不离不弃,只是让她多补补而已啊,好了,补了,而然someString也得益了,为什么?因为他们都是共同拥有一个对象呀,没办法啊,谁叫你直接把自己的对象就赋给人家了是吧,怎么办?好办~
来看一下Objective-C的深浅拷贝:
改一下代码:
NSMutableString *someString = [@"123" mutableCopy];
NSMutableString *anoterString = [someString copy];
NSLog(@"someString:%@,anoterString:%@",someString, anoterString);
[anoterString appendString:@"456"];
NSLog(@"someString:%@,anoterString:%@",someString, anoterString);
好,编译一下~~~会输出什么????
WTF,直接崩了,哈哈,为什么?看了刚刚那个连接,应该会知道,copy出来的可变类型是不可变版本的,不可变版本你还来个appendString,(runtime哥哥在方法列表中找不到有这名堂,并且给了你3次拯救的机会都没珍惜,不挂你的程序挂谁?????不知道说什么?看一下这里Runtime),而且这里想不让anoterString拥有自己的对象,本来就不应该用copy,为什么?因为copy出来的对象还是指向那个地址的呀。
再改一下:
NSMutableString *someString = [@"123" mutableCopy];
NSMutableString *anoterString = [someString mutableCopy];
NSLog(@"someString:%@,anoterString:%@",someString, anoterString);
[anoterString appendString:@"456"];
NSLog(@"someString:%@,anoterString:%@",someString, anoterString);
结果?
没错,谁叫你someString不叫你的对象补补,该是飞机场还是飞机场,我anotherString现在是有自己对象的人啊,我只是叫我自己的对象补而已嘛,是吧。
2016-03-14 00:37:33.761 Nice_Project[1597:125753] someString:123,anoterString:123
2016-03-14 00:37:33.762 Nice_Project[1597:125753] someString:123,anoterString:123456
最后来抄一下书本的要点吧:
Objective-C为C语言添加了面向对象特征,是其超集。Objective-C使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接受一条消息之后,究竟执行何种代码,由运行期环境而非编译器来决定。
理解C语言的核心概念有助于写好Objective-C程序。尤其要掌握内存模型与指针。
更多精彩,敬请关注!
(PS:我的博客是平时一些自己的总结,可能有一些存在问题的地方,各位客官多多包涵,发现有哪里有问题的尽情喷我吧,哈哈)