id myObject = [NSString string];
1、id类型---myObject可以是任意类型的对象
2、NSString* myString = [NSString string];
备注:NSString类型的变量了,如果我们在这个对象上调用NSString 类型对象不支持的方法,编译器就会发出警告
注意:在对象类型的右面有一个星号(*),在Objective-C中,所有的对象变量 都是指针类型。id类型已经被预定义为指针类型,所以不需要加一个星号。
3、创建对象两种方法
1)NSString* myString = [NSString string];这是一种更加自然的方式,通过这种方式可以创建一个自动释放(autoreleased)对象
2)NSString * myString =[[NSString alloc] init]
这是一个嵌套防范的调用,第一个是NSString 类本身alloc方法调用,这是一个相对底层的调用,作用是:分配内存及实例化一个对象,第二个是调用新创建的对象的init方法,做对象的初始化设置工作。
NSNumber* value = [[NSNumber alloc] initWithFloat:1.0]; init方法的另一种版本,带有输入参数
4、内存管理
1)编写程序时可以选择允许 垃圾回首--不是特别复杂的情况下,可以不用考虑内存管理
2)通过手工alloc的方式创建的对象,使用之后需要release这个对象
3)不能手工(release)的释放一个自动释放(autoreleased)的对象--造成应用程序崩溃
例子:
//string1 将被自动释放
NSString* string1 = [NSString string];
//必须在用完后手工释放
NSString* string2 = [[NSString alloc] init];
[string2 release];
自动释放对象---可以认为当前函数结束的时候被自动释放
5、设计类接口
objective-c的语法中:创建一个类非常简单。
一个类通常为两部分:类的接口 、 类的实现
类的接口(interface):存放在ClassName.h的文件中
类的实现:存放在ClassName.m的文件中,包含方法的世纪实现代码,通常还定义了客户类不能访问的私有(private)方法
1)接口例子:
Photo.h:
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject { NSString* caption; NSString* photographer;
}
@end
import:倒入应用程序的基本类
interface:类的声明“:”冒号后面时父类
花括号里面声明了两个实例变量:caption和photographer。都是NSString 类型,实例变量可以是任何对象类型,包括id类型。
@end符号结束类的声明。
1.1)添加get/ set方法
1.1.1)get
给实例变量添加一些获取器(getter)
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject { NSString* caption; NSString* photographer;
}
- caption;
- photographer;
@end
备注:objective-c中通常省略get方法的“get”方法前缀,如上例
减 号(-)表明该方法是一个实例方法。
加号(+),则表明该方法 是一个类(static)方法。
编译器会默认一个方法的返回值是一个id类型的对象,所有的输入参数也默认是id 类型。上述代码在技术上是正确的,但是我们一般不这样写,我们需要给这些方法指定 返回值类型。如下
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject { NSString* caption; NSString* photographer;
}
- (NSString*)caption;
- (NSString*)photographer;
@end
1.1.2)setter
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject { NSString* caption; NSString* photographer;
}
- (NSString*)caption;
- (NSString*)photographer;
- (void) setCaption: (NSString*)input;
- (void) setPhotographer: (NSString*)input;
@end
设置器不需要有返回值,所以我们指定返回值是void。
2)类实现
实现获取器/设置器(getter/setter)
#import “Photo.h"
@implementation Photo
- (NSString*) caption { return caption;
}
- (NSString*) photographer { return photographer;
}
- (void) setCaption: (NSString*)input {
[caption autorelease];
caption = [input retain]; }
- (void) setPhotographer: (NSString*)input {
[photographer autorelease];
photographer = [input retain];
}
@end
设置器需要处理两个变量,第一个时当前引用的对象,第二个输入新对象。
输入新对象前需要进行垃圾回收
在带有垃圾回收机制的环境中,可以直接直接设置成新的值:如下所示
- (void) setCaption: (NSString*)input
{
caption = input;
}
不能使用垃圾回收时需要手动的释放旧的对象retain新的对象,释放对象的引用有两种方式:
release:立刻释放对象的引用
autorelease:过会释放,但实际引用一直存在直到当前方法结束(除非特别干预)
3 init方法
init方法用来给实例化变量设置初始化值
- (id) init
{
if ( self = [super init] ) {//将[super init]的结果赋值给self。
[self setCaption:@"Default Caption"];
[self setPhotographer:@"Default Photographer"]; }
return self;
}
备注:super 父类做初始化操作,if校验在设置初始值前验证父类初始化是否成功。
4 dealloc
作用:从内存中删除对象时调用,在这个方法中释放所有对象的实例变量
‐ (void) dealloc {
[caption release];
[photographer release];
[super dealloc];
}
备注:1、释放变量时不需要使用autorelease,因为标准的release更快一些
2、super dealloc 要求父类做清理工作,如果没有这句对象就不会被从内存中删除,造成内存泄漏。
3、当启用垃圾回收机制时,对象的dealloc 方法不会被调用,需要实现一个finalize方法来替代它
6 内存管理
objective-c内存管理是基于引用计数的。释放内存工作由运行环境完成。
分配分(alloc)对象,保留(retain)的对象都需要发送release消息释放内存。如果alloc一次后又retain一次则需要release两次才能释放该对象的内存。
创建对象通常只有两个原因:
1、作为实例变量保留
2、在函数内部作为临时变量使用
回收规则:
1、大多数情况下,一个实例变量的设置器(setter)会自动释放(autorelease)原来引用的对象,同时保留(retain)新的。你只需要保证在dealloc函数中释放 (release)了它就行了。
2、如果你是通过alloc或者copy创建了一个对象,在函数结尾的地方给它发送一个release或者autorelease消息就行了。如果你是通过其它方式创建的对象,就什么 也别做。
下面是第一个例子,管理实例变量:
- (void) setTotalAmount: (NSNumber*)input {
[totalAmount autorelease];
totalAmount = [input retain]; }
- (void) dealloc {
[totalAmount release];
[super dealloc];
}
下面是另外一个例子,关于本地引用。我们只需要释放通过alloc创建的对象就行 了:
NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75]; NSNumber* value2 = [NSNumber numberWithFloat:14.78];
// only release value1, not value2
[value1 release];
下面是一个组合例子,将一个本地引用设置给实例变量:
NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75]; [self setTotal:value1];
NSNumber* value2 = [NSNumber numberWithFloat:14.78]; [self setTotal:value2];
[value1 release];
注意,不论你是不是把本地引用当成实例变量一样赋值,管理它们都是完全相同的。 你不必考虑设置器(setter)是如何实现的。
如果你理解了这些,你就理解了关于Objective-C内存管理中90%你需要知道的内容。
7 日志记录
Objective-c中用于日志输出到控制台的方法是:NSLog()函数,
%@符号代表一个对象
例子:NSLog(@“hello world”);
NSLog ( @"The current date and time is: %@", [NSDate date] );
8 属性(Properties)
属性是objective-c的一个特性,允许我们自动生成访问器,同时还有其它方面的好处。如下代码片段对比。
之前的代码看上去是这样的:
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject { NSString* caption; NSString* photographer;
}
- (NSString*) caption;
- (NSString*) photographer;
- (void) setCaption: (NSString*)input;
- (void) setPhotographer: (NSString*)input;
@end
用属性改写后的代码看上去是这样的:
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject { NSString* caption; NSString* photographer;
}
@property (retain) NSString* caption; @property (retain) NSString* photographer;
@end
@property是Objective-C语言的一个指令,通过它声明属性。带括号 的”retain”指示设置器(setter)要保留输入值,该行后面的是指定属性的类型以 及名称。
下面我们看看这个类的实现部分:
#import "Photo.h"
@implementation Photo
@synthesize caption;
@synthesize photographer;
- (void) dealloc {
[caption release];
[photographer release];
[super dealloc];
}
@end
@synthesize 指令为主动为我们生成setter和getter,我们只需要实现dealloc方法进行释放即可。
注意:只有访问器不存在的时候,@synthesize才会自动生成访问器,所以即使用@synthesize声明了一个属性,仍然可以自定义getter 和setter,编译器会只会自动生成我们没有自定义的方法。
9 Nil上调用方法
objective-c 中的nil对象的作用等同于其它语言中的null,不同的地方在于,在nil上调用方法不会导致程序崩溃或抛出异常。
调用nil对象的方法并且改方法有返回值的话,就会得到一个nil返回值。
我们也可以用它来稍微改进一下我们的dealloc方法:
- (void) dealloc
{
self.caption = nil;
self.photographer = nil;
[super dealloc];
}
可以这样做是因为当我们将nil赋值给一个实例变量,设置器(setter)会释放旧对 象并且保留(retain)nil对象。这种做法对于dealloc来说更好一些,因为这样做 避免了让变量指向一个随机的数据,而这个数据又恰好是另外一个对象。
注意,我们在这里使用了self.<var>语法,这表示我们使用的是setter,它会进 行内存管理。如果我们仅仅是直接设置值,像下面这样,那就会产生内存泄露:
// incorrect. causes a memory leak.
// use self.caption to go through setter caption = nil;
caption = nil;
10 类目(Category)
类目是objective-c中最有用的一个特性,踏允许你为已经存在的类添加一些方法而不用子类化该类,也不需要了解该类的实现细节。
作用:可以给一个内建的类对象添加方法,例如给NSString类型增加一个方法判断是不是一个url实现如下:
下面是一个实现。切记,这不是很好的检查URL的方法。我们只是为了说清楚类目 的概念。
#import "NSString-Utilities.h" @implementation NSString (Utilities)
- (BOOL) isURL {
if ( [self hasPrefix:@"http://"] ) return YES;
else
return NO;
}
@end
现在,你可以使用NSString的这个方法了,下面的代码会在控制台打印“string1 is a URL”:
NSString* string1 = @"http://pixar.com/"; NSString* string2 = @"Pixar";
if ( [string1 isURL] )
NSLog (@"string1 is a URL");
if ( [string2 isURL] )
NSLog (@"string2 is a URL");
与子类不同,你不能通过类目来添加实例变量。但是你能通过类目重写(override) 类中已经存在的方法,当然,重写的时候要特别小心。
记住,当你通过类目更改一个类的时候,这个更改会影响你这个应用程序中所有这 个类的实例。