这篇文章将会对ObjectC语言中比较难理解的概念进行汇总,帮助我们更好的进行iOS开发。
方法&成员变量&属性
我们首先看一个很简单的方法代码:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;
- 方法名称你可以认为是dequeueReusableCellWithIdentifier_forIndexPath;
- 参数有两个,第一个是NSString *类型,名字叫identifier;第二个是NSIndexPath *类型,名字叫indexPath;
- 最顶头的减号("-")标识是object方法,如果是加号("+")标识是静态(static)方法;
- 属性通过@private定义,并且需要你自己去显式通过实现方法来存取,或者.h文件中指定@property,.m文件中指定@synthesize来使得可以通过self.propertyName访问;
- 可以指定readonly指示符指示仅生成get方法;
- 注意对于静态方法(类方法)可以使用self或者super关键字指定使用自己还是父类的静态方法;
属性
对于属性中可能使用到的几个关键字我们这样理解:
- nonatomic关键字说明这个是非原子操作的,所以可能是非线程安全的;
- strong和weak关键字在所指定的属性是对象类型时使用。strong表示保持这个对象的指针所指向的内存,一直到使用结束;weak表示如果没有人再使用这个指针,那么就设置为nil;官方的解释是这样滴:
strong is the default. An object remains “alive” as long as there is a strong pointer to it.
weak specifies a reference that does not keep the referenced object alive. A weak reference is set to nil when there are no strong references to the object.
另外还有一个比喻:
我们的属性可以认为是一条狗,他总是想逃跑;strong可以认为是狗链子,我们可以用多条链子拴住这条狗,只要有一条还抓在我们手里,狗就跑不了;而weak是一个看着狗的孩子,当链子在我们手中的时候,孩子可以看住狗,但是当链子不在我们手中时,狗就可以跑掉,尽管孩子在看着狗。
.h
注意obj2是strong还是weak对程序运行结果的影响。@interface ViewController : UIViewController @property (nonatomic, strong) MyType *obj1; @property (nonatomic, weak) MyType *obj2; @end
.m
注意weak是在iOS5以后引入的,所以如果你的代码需要在iOS4上面运行,你只能使用strong。- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. obj1 = [[MyType alloc] init]; obj2 = obj1; obj1 = nil; NSLog(@"obj2 = %@", obj2); }
- 上面讲得实际上就是引用计数的概念,strong类型的对象会被加上引用计数,当最后一个对象不再保留引用计数的时候,strong的对象就会被自动释放,所以这个时间点相对垃圾回收机制来说是可预期的;
- 局部变量由于在方法执行结束后超出其作用于,所以会被自动清除引用计数;
- 对于非ARC的程序,weak对应的关键字是unretain,strong对应的是retain关键字;另外还有一个关键字unsafe_unretain这个会造成野指针,不推荐使用,就不赘述了;
- 所有的属性,在你没有设置默认的setter的时候,初始化的值都是0;
- 可以使用if (obj)判断对象是否是nil,但一般情况下你无须这样去做。对一个nil的对象做一个操作(例如调用一个方法)将不会真正去执行这个方法,如果这个方法是有返回值的,一般都会返回0;但是如果这个方法是返回一个C类型的结构值的,那么你需要自己去事先检查这个对象是否是nil,因为无法保证返回的结构中的成员变量是有效的还是无效的,可能是一堆垃圾数据;
对于如何使用方法/成员变量/属性,建议采用如下规则:
- 所有的实例变量都声明为:@private;
- 所有属性都是用@property定义,并在方法中采用self.property或者super.property的方式引用;
- .m文件中使用@synthesize使得编译器将setter和getter设置为与property同名的方法;
- 注意有这样的方法,可以强制你必须在代码中使用self.property的方式使用属性,在.m文件的interface段内这样写:
@interface MyViewController () @property int myPriveteProperty; @end
这样你在方法中必须使用self.myPrivateProperty这样的方式来引用成员变量。 - 注意在对象使用前最好检测是否是nil,因为对于object.property的使用形式,如果object本身是nil,并且property本身是一个CType的一个结构类型变量,那么property本身的值是未知的,可能会导致不可预知的错误;
- 对于需要打印输出的对象,可以重写-(NSString *)description方法(类似于java中的toString),并且可以在NSLog()中使用%@输出;
变量类型
对于变量类型有几个需要注意的地方:
- Java中我们是可以定义一个String类型的变量(或者任意一个对象类型的变量),但在ObjectC中我们的对象必须使用指针类型定义:NSString *str;
- id类型返回任意一个对象类型的变量,类似于Object。但注意:返回的仍然是一个指针,类似于NSString *;
- 事件响应方法:IBAction返回值类似于void;
- 注意BOOL类型的变量是YES/NO,不再是TRUE/FALSE;
访问控制符
访问控制符有几个需要注意的,我列举如下:
- ObjectC的默认访问控制符是@protected的,当然你也可以定义为@private或者@public;
动态类型Dynamic Type
这里面最需要讨论的就是内省(Introspection):
- isKindOfClass对于有java经验的我来说很好理解,可以理解为一个类是否是一个另一个类或其父类的实例;
- isMemberOfClass就比较诡异了,是返回一个object是否这个类的实例,注意这里不检测继承关系,也就是说[teacher isMemberOfClass: [Teacher class]]返回YES,但是[teacher isMemberOfClass: [Person class]]返回NO。
注意看下面的代码:
obj1 = [[MyType alloc] init]; NSLog(@"obj1 isKindOfClass:NSObject = %@", ([obj1 isKindOfClass:[NSObject class]]) ? @"YES" : @"NO"); NSLog(@"obj1 isMemberOfClass:NSObject = %@", ([obj1 isMemberOfClass:[NSObject class]]) ? @"YES" : @"NO"); NSLog(@"obj1 isMemberOfClass:MyType = %@", ([obj1 isMemberOfClass:[MyType class]]) ? @"YES" : @"NO");
- ObjectC中有一种特殊的表达式:[tester respondsToSelector:@selector(test:)],其中的@selector是一个SEL类型,前面的表达式表示检查tester这个object有没有提供一个名字叫"test"的方法,注意表达式上"test"后面是有冒号的;
注意另一个问题,如果一个对象被设置为nil,那么以上的这些操作对于这个对象来说返回都是NO;
集合类型
集合类型使用的时候需要注意:
- NSArray/NSDictionary/NSSet等都是不可变的集合类型,意思是在创建的时候就初始化内容,并且以后不可以再增加/删除内容;
- NSMutableArray/NSMutableDictionary/NSMutableSet等是可变集合,分别类似于Java中的数组/Map等集合类型;
总体来说,ObjectC和C/C++/Java这些语言有类似的地方,但差异较大,过渡过来的学习曲线还是比较陡峭的,但只要抓住差异注意理解,上手还不是那么难,关键在于理解。