在第二课中,介绍了Objective-C的一些关键知识点,包括点访问方法,strong和weak的比较,nil,实例化,动态绑定技术(程序在执行时才会知道要执行的代码有没有),自省(选择器的使用),基础的框架如NSObject,NSString等等。
这些是开发的基础,整理了一遍有利于程序的开发和调整。像类方法只能使用类来调用,实例方法只能使用实例,这点以前还没有明白。对于在实例中怎么不能调用类方法的问题还疑惑了一阵子。
1、点符号方法访问属性变量
在property引进后开始使用这个方法,苹果官方解释为这样做是出于代码美观考虑。苹果的产品将美观放在第一位,这产品包括了obj-c的代码。
使用点符号法self.propery访问@property比普通的[self property]方法更直观。在C结构体中,也是使用点符号方法访问属性。这两个方法看上去很相似,但它们有两点大不同:
其一,我们不能发送消息给C结构体,因为C结构体根本没有可执行的方法。
其二,C结构体绝不分配在heap中,因此不能通过指针来访问到它。(注,它分配在stack中。内存管理中分为heap和stack)
2、strong 和 weak
在定义@property时,会有选项如strong、weak等。
strong表示保存这个对象在heap中直到不再有任何指针指向它。(注,我理解为所有指向它的指针不先设置为nil,它就不会从heap中清理。)
weak表示保存这个对象在heap中,只要其他人strongly指向了它。如果它从heap中清理了,会将所有指向它的指针调整为指向nil。这个功能从iOS5开始使用。(注,我理解为如果它从heap中清理后,所有指向它的指针都会自动调整为nil。)
但是这种清理heap的机制不是垃圾回收机制,在iOS上没有这种内存自动回收机制。它采用的一种更好的机制,称之为引用计算(reference counting)。这种机制可以展开来讲很多,但在iOS中又有了新机制,称为自动应用计算(ARC)。了解它可以有助于理解内存管理。
在iOS中还有一种内存释放方法,为dealloc。
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
这种方法很少会用到,因为如果用到它,则太迟了。估计iOS已经将应用给杀死了。
3、nil
这是一个对象指针的值,表示不指向任何对象。它和原始类型如int double的“zero“很相似。
在实例变量中,作为一个对象指针类型定义的,其初始值都是nil。如
id obj = nil;
NSString *hello = nil;
nil还有一个隐藏的功能,就是它可以放在if语句中做条件判断。
如if (obj) {run something;}这个语句,obj为nil时则不执行,不为nil则执行。
对于指针值为nil的实例变量,任何调用该对象中方法,都不会出错,没有任何方法会被执行。这可以理解为向nil发送消息时,没得执行啊。
该操作的返回值是0。如果int i = [obj methodWhichReturnsAnInt];则没有问题,返回结果是0,属于int类型;如果CGPoint p = [obj getLocation];则就有问题了,返回结果还是0,而0不是CGPoint类型。CGPoint是一个C结构体。(注,类型不一致,会不会crash?)
4、BOOL类型
在Objective-C,也有一种布尔类型,实质上它是typedef。
它可以用来做条件判断
if (flag) { }
if (!flag) { }
YES值意思为true,NO值意思为false
N0值等于0,YES值除0之外所有的数字
5、实例方法和类方法
这个知识点很重要,在obj-c中使用范围很广。他们之间区别有下列几点需要说明。
第一,命名方式上的差别,实例方法是以“-”开始,类方法是以“+”开始。
- (BOOL)dropBomb:(Bomb *)bomb at:(CGPoint)position from:(double)altitude;//这是实例方法在.h文件中的声明
+ (id) alloc;//这是类方法
+ (Ship *)motherShip;
+ (NSString *)stringWithFormat:...
第二,调用语法的差别。
实例方法只能实例去调用,语法为[<pointer to instance> method]。
例子:
Ship *ship = ...; // instance of a Ship
destroyed = [ship dropBomb:firecracker at:dropPoint from:300.0];
同样,类方法只能用类去调用,语法为[Class method]。
例子:
Ship *ship = [Ship motherShip];//Ship是一个类
NSString *resultString =[NSString stringWithFormat:@“%g”, result];//NSString也是一个类型
[[ship class] doSomething];//ship是一个实例化的对象,必须使用[ship class]转换成类后才能调用类方法,而直接使用ship实例无法调用到类方法。
(注,首字符为大写的都是类,小写的则是实例化的对象,这是命名规则)
第三,self/super的区别
在类中,使用self/super的结果都是类;
在实例中,使用self/super的结果都是实例;
6、实例化(Instantiation)
其一,使用其他对象创建自己的对象,如
NSString’s - (NSString *)stringByAppendingString:(NSString *)otherString;
NSString’s & NSArray’s - (id)mutableCopy;
NSArray’s - (NSString *)componentsJoinedByString:(NSString *)separator;
其二,也不是所有的使用其他对象创建而生成的对象都是新建的,有的对象会只是一个指针,而不是在heap中再划分内存创建对象。如,
NSArray’s - (id)lastObject;
NSArray’s - (id)objectAtIndex:(int)index;
但是,如果使用copy关键字,则会创建新对象。
其三,还可以使用类方法创建对象,如
NSString’s + (id)stringWithFormat:(NSString *)format, ...
UIButton’s + (id)buttonWithType:(UIButtonType)buttonType;
NSMutableArray’s + (id)arrayWithCapacity:(int)count;
NSArray’s + (id)arrayWithObject:(id)anObject;
其四,使用Allocating 和 initializing创建新对象
例如:
NSMutableArray *stack = [[NSMutableArray alloc] init];
CalculatorBrain *brain = [[CalculatorBrain alloc] init];
Allocating是通过NSObject的类方法alloc在heap中分配一个空间给新对象。如@synthesize就是执行这个操作。因此所有的@sythesize的对象都已经在heap中分配了一个足够大的内存空间。但这只是第一步,如果要使用该对象,还必须初始化,否则会直接crash。
Initializing对于多数类而言,都有很多方法,但NSObject类只有一个初始化方法为init。
例如:
- (id)initWithFrame:(CGRect)aRect; // initializer for UIView
UIView *myView = [[UIView alloc] initWithFrame:thePerfectFrame];
- (id)initWithCharacters:(const unichar *)characters length:(int)length;
- (id)initWithFormat:(NSString *)format, ...;
- (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;
在开发类时,初始化方法中的返回类型建议设置成id,这样做是为了继承。如果它的子类使用它的初始化方法时,直接使用id类型即可。
例如:
@implementation MyObject
- (id)init
{
self = [super init]; // call our super’s designated initializer
if (self) {
// initialize our subclass here
}
return self;
}
@end
7、动态绑定
其一,所有的对象都被分配在heap中,所以你一直可以使用指针
例如:
NSString *s = ...; // 静态类型定义
id obj = s; // 非静态类型定义,但完全合法,不要使用”id *”因为它表示一个指针的指针。
其二,在runtime时才会知道运行的代码是什么
静态类型如第一点所说的NSString *和id的比较,静态类型只不过在编译时更有利于我们发现bug。
如果runtime时,没有找到所运行的代码,就crash。但我们可以使用“introspection”去判断代码是否可以执行。
8、自省(introspection)
自省是对象的固有能力,即程序运行时,对象可根据请求对外透露自身基本特性。您可以给对象发送某种消息,向其询问与它自身相关的问题,对象在Objective-C 运行时将会向您提供答案。自省是很重要的编码工具,它可以让程序变得更加高效健壮。
所有的继承了NSObjec的对象有拥有下列三个方法:“
isKindOfClass: returns whether an object is that kind of class (inheritance included)
isMemberOfClass: returns whether an object is that kind of class (no inheritance)
respondsToSelector: returns whether an object responds to a given method
方法1的参数值为[NSString class],即发送一个名为class的类方法给一个类而得到。例如:
if ([obj isKindOfClass:[NSString class]]) {
NSString *s = [(NSString *)obj stringByAppendingString:@”xyzzy”];
}
方法3的参数值为一个选择器,如@selector(shoot)。
如果对象中有这个方法shoot,就响应为true。例如:
if ([obj respondsToSelector:@selector(shoot)]) {
[obj shoot];
} else if ([obj respondsToSelector:@selector(shootAt:)]) {
[obj shootAt:target];
}
介绍一种新类型SEL,它是Objective-C用于选择器的类型。
SEL shootSelector = @selector(shoot);
SEL shootAtSelector = @selector(shootAt:);
SEL moveToSelector = @selector(moveTo:withPenColor:);
在NSObject类中,可以通过方法performSelector: 或者 performSelector:withObject:来执行选择器。例如:
[obj performSelector:shootSelector];
[obj performSelector:shootAtSelector withObject:coordinate];
在NSArray类中,可以通过makeObjectsPerformSelector:来执行选择器操作。例如:
[array makeObjectsPerformSelector:shootSelector]; // cool, huh?
[array makeObjectsPerformSelector:shootAtSelector withObject:target]; // target is an id
在UIButton类中,可以通过 - (void)addTarget:(id)anObject action:(SEL)action ...;方法来执行选择器。例如:
[button addTarget:self action:@selector(digitPressed:) ...];
9、基础框架
NSObject
在IOS SDK中,NSObject是基础类,实现一些方法,如自省的方法等等。
- (NSString *)description is a useful method to override (it’s %@ in NSLog()).
- (id)copy; // not all objects implement mechanism (raises exception if not)
- (id)mutableCopy; // not all objects implement mechanism (raises exception if not)
还有这些常用架构,如NSString,NSMutableString,NSNumber(原始类型 int, float, double, BOOL等等的封装成对象),NSValue,NSData,NSDate。
还有一些集合类型如NSArray,NSMutableArray,NSDictionary,NSMutableDictionary,NSSet,NSMutableSet,NSOrderedSet,NSMutableOrderedSet。这些分成三种类别,分别为array,dictionary,set。
这些类可以使用for in 方法遍历访问他们的成员。
还有Property List和NSUserDefaults两个,也需要关注一下。