Chinaunix首页 | 论坛 | 博客
  • 博客访问: 109351
  • 博文数量: 29
  • 博客积分: 447
  • 博客等级: 下士
  • 技术积分: 414
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-05 23:03
个人简介

整天捣鼓嵌入式,兼职搞搞iOS,这么折腾为了啥?都是为了俺的娃!

文章分类

分类: iOS平台

2015-01-22 14:48:46

一、OC简介
 1.本文简称Objective-C为OC.
 2.OC就是C语言,只不过其对C进行了扩展,使之有了面向对象的概念.
 3.OC完全兼容C语言语法,也就是说在.m文件中,你完全可以使用C语言。当使用C语言风格的函数时,
   函数不能像OC的方法那样使用消息机制来调用,同样struct定义的结构体也不具备对象的属性,不
   像C++中的结构体本身就是对象。
 4.C风格的函数我们称为函数,OC中类成员函数我们称之为方法,以示区别.
 5.同时OC与C++也可以混合使用,这样的程序称作Objective-C++程序,程序文件名后缀为.mm.
   OC不支持命名空间,类名必须是唯一的,惯用做法是类名加前缀,CoCoa框架中很多前缀,如NS,CF...

二、基本数据类型
 类型    范围        格式化字符串
 char    8bits       %c
 int     32bits      %i,%o,%x,%#x
 float   32bits      %f,%e,%g
 double  64bits   %f,%e,%g
 id   任何对象    %p
 数据类型限定词
 signed,unsigned:有符号和无符号整数,常量后加u/U,编译器会保证该数为无符号数
 short:  16bits,格式化字符串为"%hi","%ho","%hx"
 long int:大部分系统与int一样位宽(32位),格式化使用"%li",常量后加l/L,如131071100L.
 long long int:64bits,格式化使用"%lli".
 long double:格式化使用"%Lf","%Le","%Lg",常量后加l/L,如1.234E+7L.
 格式化字符串说明:
 1.%#x与%x是等效的.
 2.%g是NSLog格式,允许NSLog确定使用科学计数法(%e)还是浮点计数法(%f)显示,
   如果值x<-4或者x>5,则采用%e表示,否则采用%f表示。
 3.16进制的浮点常量用p/P表示,如0x0.3P10 = 3/16 * 2^10
 4.id表示未知对象,是objective-c多态和动态绑定的基础,id=nil表示id指向空对象,nil相当于C语言中
   的NULL.还有一个与id类似的关键字instancetype,也可以作为方法的返回值,但不可以作为参数,而id
   则可以作为参数,instancetype可以使那些非关联返回类型的方法返回所在类的类型,编译器可以检测
   出instancetype类型的对象是否具有某些方法,而编译器无法检测id类型的对象所具有的方法。
 5.布尔类型:BOOL,值为YES/NO.其实质是typedef char BOOL来实现的,它的定义在Foundation.h中.
 6.C类型的字符串前加@表示将字符串转换为NSString类型
 7.C类型的字符串必须使用"%s"进行格式化输出,NSString类型的字符串则使用"%@"。
 8.引用类型或指针类型的对象表示时使用指针的形式,如定义一个NSString对象:
   NSString *str = @"hello!";
   而在C++中,定义一个string: string str = "hello!", string *str则表示的是一个对象指针。
   对于基本类型,如NSInteger,则直接使用普通变量的形式(因为:typedef long NSInteger;):
   NSInteger number = [[NSNumber initWithInteger:100] integerValue];
   基本类型数据一般是存储在栈中,而引用类型数据需要使用alloc或new创建,存储在堆中。
三、方法/函数
 -(void) func:(int)x andY:(int)y ...
 |  |     |     |  |   |    |  |
 |  |     |     |  |   |    | 参数名...
 |  |     |     |  |   | 参数类型
 |  |     |     |  |参数2标识符
 |  |     |     | 参数名
 |  |     |参数类型
    |  |  方法名(我们可以将它看成是参数1标识符)
 |方法返回值类型
  方法类型(+类方法,-实例方法)
    例: 
 -(void) buildBox:(int)x andY:(int)y andZ:(int)z{...}
 在C++中函数重载是利用了参数类型来识别,我们会这样写上面的函数:
 void buildBox(int x,int y, int z){...}
 C++编译后函数名实际函数名称变为了buildBox_int_int_int这样的形式.而在OC中,是利用参数
 标识符来实现方法的重载的,OC编译后函数名称变为了buildBox_andY_andZ这样的形式.我们调用
 方法时应该这样写:[xxx buildBox: 3 andY: 4 andZ: 5],实际上我们可以讲方法名看作是第一
 个参数的标识符。方法名称实际上是由所有参数标识符构成的,":"为参数分割符。
 当然,也可以不写参数标识符:
 -(void) buildBox:(int)x :(int)y :(int)z{...}
 那我们调用方法的时候也只能这样调用: [xxx buildBox:3 :4 :5],这样的写法就没法清晰的
 知道参数的意义了,因此不推荐省略方法的形参名称。
 在OC中方法中没有C++中public,protected,private的概念,只有类似于static的概念,方法类型
 为"+"表示调用该方法时不需要生成对象实例,而"-"表示只有该类的实例才可以调用该方法。

四、类
 1.定义类接口(*.h文件)
 @interface Demo
 {
  int x;
  int y;
  ...
 }
 @property (readonly,noatomic) int x,y;
 ...
 @end
 
 2.类实现(*.m文件)
 @implementation Demo
 ...
 @end
 
 3.类的成员变量默认为protected的,也可以使用以下指令指定成员变量的访问属性:
   @public:自己、子类其他类都可以访问.
   @protected:自己及子类访问,其他类不能访问.
   @private:自己访问,子类和其他类都不能访问.
   @package:仅供在该类所在的可执行文件镜像中被访问,其他可执行镜像中不可访问.
 
 4.自动合成成员变量的set/get方法
   @property (attr1,attr2...) int x,y;  /* 在h文件接口定义中声明变量属性 */
   @synthesize x,y;    /* 在m文件中自动合成方法 */
   OC中允许使用"."运算符来访问和设置成员变量.
   attr1,attr2是变量的属性列表,属性主要分为三类:
   读写属性:  (readwrite/readonly)
   setter语意:(assign/retain/copy)
   原子性:    (atomicity/nonatomic)
   关联性:  (strong/weak)
   各属性意义如下:
   readwrite: 产生setter\getter方法
      readonly:  只产生简单的getter,没有setter。
   assign:    默认类型,setter方法直接赋值,而不进行retain操作
   retain:    setter方法对参数进行release旧值,再retain新值。
   copy:      setter方法进行Copy操作,与retain一样
   nonatomic: 禁止多线程,变量保护,提高性能
   strong:
   weak:
  
 5.self关键字,相当于C++中的this,但是self不是一个实例变量,它可以被类方法调用.与之对应的是super关键字,
   代表该类的父类对象.
 
 指令用于前向申明此类型为一个类,而不用去import该类的头文件,但是当需要使用该类的
   的方法是,则需import类的头文件,否则编译器无法知道类的方法定义.
  
五、选择器
 @seletor类似于C语言的函数指针,是OC实现动态绑定和多态的一个基础特性。在OC中,程序编译时,编译器将
 所有的方法名称写到一个表中,每个方法分配一个标识符,同名的方法(不同类)共用一个标识符。
 SEL即代表方法的标识符。
 SEL func = @selector(buildBox:andY:andZ:);
 
 与SEL相关的还有一个IMP变量.
 
六、分类和协议
 1.分类用于扩展类的功能,分类可以访问类的成员变量,但不能为类新增成员变量。分类所定义的方法,可以实现,
   也可以不实现。分类命名必须是唯一的。
    2.协议相当于Java中的接口,只定义不实现。需要由使用它的类来实现。可以使用关键字@required和@optional来
   指定协议中必须实现的方法和可选实现的方法。协议中默认的方法都是@required的。
 3.非正式协议,其实质就是分类。
 
七、重载
 由于OC是根据方法名来识别函数的,重载时只要求方法名不相同,参数可以相同。比如
 -(void) test:(int) intValue;
 -(void) test:(float) floatValue;
 上述两个函数在oc中属于重复定义,不是重载,因为它们的名字都是"test:"。
 所以在oc 中的重载应该是指参数个数不同,但是返回类型后面的名字相同。
 或者是 参数个数相同,但是其方法名不相同。
 -(void) test:(int) intValue;
 -(void) test:(float) floatValue; //错误,与第一个方法重名(test:)
 -(void) test:(int) x andY:(int)y;
 -(void) test:(int) x andY:(double)y;//错误,还是重名了(test:andY:)
 -(void) test:(int) x :(double)y;  //正确,名字不同

八、块(BLOCK)语法
 代码块本质上是和其他变量类似,相当于Java中的匿名函数.不同的是,代码块存储的数据是一个函数体。
 使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值.
 void (^printBlock)(NSString *x);
   |   |  |         |      |
   |   |  |         |   参数名
      |   |  |   参数类型
   |   |   块对象 
   | 块的语法标志
  块对象返回值
 printBlock = ^(NSString* str) 
 { 
  NSLog(@"print:%@", str); 
 }; 
 printBlock(@"hello world!"); 
 在使用block时几个重要点:
 1.block函数外的对象,在block语句块内只有可读访问权限。
 2.对外部对象进行__block申明解决了问题1.
 3.各类型的变数和block之间的互动.
 extern NSInteger CounterGlobal; 
 static NSInteger CounterStatic; 
 { 
  NSInteger localCounter = 42 ; 
  __block char localCharacter; 
  void (^aBlock)( void ) = ^( void ) 
  { 
   ++ CounterGlobal ; //可以存取。  
   ++ CounterStatic ; //可以存取。   
   CounterGlobal = localCounter; //localCounter在block 建立时就不可变了。  
   localCharacter = 'a' ; //设定外面定义的localCharacter 变数。  
  }; 
  ++localCounter; //不会影响的block 中的值。  
  localCharacter = 'b' ; 
  aBlock(); //执行block 的内容。  
  //执行完后,localCharachter 会变成'a'  
 } 

九、内存管理&ARC
 1.OC中有两类对象,一类是结构体(包括基本类型数据),一类是NSObject对象(即我们所说的引用类型数据).
   对于结构体的赋值操作,会创建一个源对象的副本(即一个新对象),而对于引用类型数据的赋值操作,则不
   会创建对象副本,而是使用其引用(即指针),赋值后两个引用对象都指向同一个内存地址.
   TestObject *obj1 = [[TestObject alloc] init];
   TestObject *obj2 = obj1;
   对象变量的实质是指针,所以obj2与obj1指向的仍是同一个对象.请牢牢记住这一点.
   对于引用类型数据,当我们在一个地方引用后,需调用retain方法将其引用计数加1,当我们不再引用时,
   需调用release方法将其引用计数减1,这样将可以防止引用无效的对象。如我们调用了一个函数,这个函数
   使用到了obj1,我们就需要在这个函数中retain一下obj1,退出函数时release掉obj1,如果不这样做的话,
   万一在函数外已经释放掉了obj1,则此函数的obj1将是一个无效的引用.(请自行脑补C语言中类似的指针使用法).
 2.将对象添加到任意一种数据集合中(使用addObject方法),引用计数都会被自动加1.
 3.使用常量字符串初始化的不可变字符串对象(NSString)的内存空间分配与其他对象不同,它没有引用计数机制,
   程序运行期间都不会被释放,其引用计数值永远为0xffffffff.这一点与NSMutableString不同
 4.强引用(strong)、弱引用(weak):如果一个对象完全属于另一个对象,则使用强引用;如果一个对象可以被多个
   对象引用,则使用弱引用。strong相当于retain,weak相当于assign(但weak会在对象释放后归零,对象变为nil)。
   只要对象仍存在别的对象强引用了该对象,该对象就不能被释放,如果仅有弱引用指向该对象,则该对象可以被释
   放,指向该对象的引用则变成了nil。

十、OC消息机制的原理
 在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段。编译器会将消息发送转换成对
 objc_msgSend方法的调用。objc_msgSend方法含两个必要参数:receiver、方法名(即:selector),如:
    [receiver message]; 将被转换为:objc_msgSend(receiver, selector);
    objc_msgSend方法也能hold住message的参数,如:
    objc_msgSend(receiver, selector, arg1, arg2, …); 
    objc_msgSend方法会做按照顺序进行以下操作,以完成动态绑定:
 1.查找selector所指代的程序(方法的真正实现)。因为不同类对同一方法有不同的实现,所以对方法的真正实现
   的查找依赖于receiver的类
 2.调用该实现,并将一系列参数传递过去
 3.将该实现的返回值作为自己的返回值,返回之

    消息传递的关键是,编译器构建每个类和对象时所采用的数据结构。每个类都包含以下两个必要元素:
 一个指向父类的指针;
 一个调度表(dispatch table)。该调度表将类的selector与方法的实际内存地址关联起来。
    每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类。
 当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)
 中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针super找到父类,并在父类的调度表
 (dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的
 内存地址调用该实现。通过这种方式,message与方法的真正实现在执行阶段才绑定。

    为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的
 缓存,缓存包含有当前类自己的selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统
 首先检查receiver对象的缓存。缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点。
 
十一、OC中的一些注意点
 1.new与alloc init的区别:
   在C++中我们用new来创建对象,在OC中,我们也可以使用new方法来创建对象:
   [MyObj new]
   虽然OC支持new方法,但这种写法比较少见,我们一般使用正统的创建对象的方法:
   [[MyObj alloc] init]
   这两种方法是有着细微的差别的:
   1).new方法只能采用默认init来初始化对象,而alloc方式则可以使用自定义的init方法来初始化对象,如
  initXXX系列函数.
   2).alloc分配内存时采用_zoneAlloc方法,将相关联的对象分配到相邻的内存区域,以便调用时消耗更少的代价,
     提升程序处理速度,而new调用的是普通的_alloc方法.
 2.对象初始化的细节:
   在OC中,默认初始化函数为init.
 3.浅拷贝与深拷贝:
   对于非容器类对象,如果对一个不可变对象进行复制,copy是指针复制,即浅拷贝:而mutableCopy则是对象复制,
   即深拷贝;如果是对可变对象复制,都是深拷贝,但copy返回的对象是不可变的.

阅读(1143) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~