oc总结 --oc基础语法相关知识

m是OC源文件扩展名,入口点也是main函数,第一个OC程序:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])

{

theme manager

@autoreleasepool {

NSLog(@"Hello, World!");

}

return 0;

}

预处理指令import会自动检验头文件有没有被包含过,防止重复包含,NSLOG是日志输出,OC字符串以@开头,自动换行,int类型的占位符是@i。OC所有关键字以@开头,@autoreleasepool与内存管理有关。

OC中的类分两个文件,.h用来声明类的变量和函数,

.m文件负责实现,与.h配合使用。OC中最根本的类叫NSObject,OC是单继承的。声明类以@interface开头,以@end结尾,实现类用@implementation开头,以@end结尾。继承用冒号。OC当中使用一个类时,导包就是#import一个类的头文件。

声明类时,成员变量要声明在大括号中,方法声明在大括号外,如果是对象方法要写-号,静态方法要写+号,所有在.h文件当中声明的方法都是公共方法,凡是类型,都要写括号,在方法名后,一个参数要一个括号,如:

//Student.h

#import <Foundation/Foundation.h>

@interface Student : NSObject {

int age;

}

-(int)getAge;

-(void)setAge:(int)age;

@end

实现类时,首先要导入.h的声明.

//Student.m

#import "Student.h"

@implementation Student

- (int)getAge {

return age;

}

- (void)setAge:(int)newAge {

age = newAge;

}

@end

对象的创建需要调用类的静态方法alloc分配内存,调用静态方法要写[],里面是类名和方法名,返回值需要用指针来接收,也就是说OC中的对象都要写个*,比如这句话调用了Student的一个静态方法alloc分配内存,并返回了一个指针来接收,其实alloc方法返回的是id类型,可以暂时理解为任何类型:

Student *stu = [Student alloc];

分配内存后要调用一个动态方法进行初始化,相当于stu指针给Student发送了一个init消息:

stu = [stu init];

也就是说定义一个变量需要两句话,但是很麻烦,所以可以连起来写,这种方法最常用:

Student *stu = [[Student alloc] init];

OC不支持垃圾回收,需要用后释放:

[stu release];

调用方法不用括号:

[stu setAge:100];

整个调用代码:

#import <Foundation/Foundation.h>

#import "Student.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

Student *stu = [[Student alloc] init];

[stu setAge:100];

int age = [stu getAge];

NSLog(@"%i", age);

[stu release];

}

return 0;

}

实际上,OC中的get方法不推荐使用get前缀,都是直接写变量名,比如[stu age];。

如果要定义有多个参数的方法可以:

- (void)setAge:(int)newAge andNo:(int)newNo{

age = newAge;

no = newNo;

}

调用:

[stu setAge:100 andNo:1];

也就是说方法名可以拆成多个部分。

注意,这里的方法的方法名是setAge:andNo:,冒号也是方法名的一部分。

OC也支持点语法,比如:person.age = 10;

不过这句话并不是java那样给成员变量赋值,而是调用了对应的set方法,在编译的时候会自动转化为方括号的语法。如果点语法在等号左边,则调用set方法,在右边则调用get方法。为了和成员变量加以区分,OC推荐成员变量都以_开头。在set方法当中,绝对不能调用self.age=newAge,根据上面的解释,这种写法会死循环。同样在get方法里也不能return self.age。在OC当中用点语法来表示get/set方法。

在点语法当中,

OC中的init是一个对象方法,返回id类型,可以自己写构造方法,自己写构造函数的时候需要先调用父类构造函数,由于在内存分配时可能失败,所以要判nil,严格的实现写法如下:

-(id)initWithAge:(int)age andNo:(int)no {

if (self = [super init]){

self.age = age;

self.no = no;

}

return self;

}

调用如下:

Student *stu = [[Student alloc] initWithAge:10 andNo:2];

注意OC当中构造函数并没有要求函数名和类名一样,其中点语法调用的是set方法,不存在死循环问题。

如果没有构造方法,可以用下面的语法来创建对象,这是个简写的语法,不推荐这么用。

Student *stu = [Student new];

可以通过NSLog(@"%@", stu);语句打印一个对象的内存地址,如果想自己重写输出格式,需要复写如下方法:

-(NSString *)description{

NSString *str = [NSString stringWithFormat:@"age is %i and no is %i", self.age, self.no];

return str;

}

可以让一个变量自动释放内存,需要多调用一个autorelease方法,比如:

Student *stu = [[[Student alloc] initWithAge:10 andNo:2] autorelease];

通常情况下,使用系统自带的一些静态方法创建的对象都是可以自动释放的。

OC的成员变量默认是protected的,子类可以访问。对于成员变量,提倡使用get和set方法。OC访问权限只有@public,@protected和@private三种,可以如下声明:

@interface Student : NSObject {

@public

int _age;

int _no;

}

这样声明的话,两个变量全是public的,这样就需要在外部使用一些C++语法访问了,是不提倡这样做的。

关于方法的权限,因为别人要使用你的类,所以包含在.h文件里的就是公有方法,如果直接把方法写在.m文件中,就是私有方法了。

如果想直接访问成员变量,那么它首先是public的,然后需要使用->符号访问。

在OC方法当中,动态方法当中使用self,谁调用这个方法self就是谁,在静态方法当中,self代表的是当前类。

实际上,OC自身提供了属性机制:

#import <Foundation/Foundation.h>

@interface Student : NSObject {

int _age;

}

@property int age;

@end

当编译器遇到原型关键字时,会自动把这行代码展开成get和set方法的“声明”。但是.h里没有实现,所以需要在.m里用一句话实现:

#import <Foundation/Foundation.h>

#import "Student.h"

@implementation Student

@synthesize age;

@end

更灵活的一点是,如果通过synthesize实现了get和set方法,那么如果在.h文件里找不到同名的变量,会自动生成一个“同名”的“私有”的成员变量,所以在.h文件里这个成员变量也可以省略了。

也就是说@synthesize age生成的成员变量是age,但是为了区分成员变量和get方法,通常习惯把成员变量定义成_age,所以为了让生成的age使用_age,所以要改成:

@synthesize age = _age;

在这种方式下,是不会生成一个叫age的同名成员变量的,这时会生成一个叫_age的成员变量(注意是私有的而不是保护的)。也就是说一个完整的封装是:

@interface Student : NSObject {

int _age;

}

@property int age;

@end

#import <Foundation/Foundation.h>

#import "Student.h"

@implementation Student

@synthesize age = _age;

@end

在XCode4.5以上的版本当中,连synthesize那一句都可以不写了,直接在.h里声明一个变量就完事了,而且在这种方式下,访问的是私有的_age成员变量!

如果我们手动实现了get或set方法,那么synthesize就不会生成对应的方法了。

所有继承了NSObject的对象都需要内存管理,OC在对象的内部维护一个整数作为引用计数器,当它为0时会回收这个对象。当对象创建完时(alloc,new,copy),这个数字为1。给对象发送retain或release消息,可以让计数器+1或-1.当一个对象计数器为0时,系统会自动调用dealloc消息,可以重写这个方法做内存释放控制,重写时在最后面调用父类的这个方法,但是不要人工调这个方法。可以发送retainCount消息获取引用计数器值。在对象被回收之后,不要多次release,否则会导致野指针错误。

OC的ARC机制全称自动引用计数,意思是把所有release之类的工作全交给系统来管理。

对于对象的聚合/组合,比如Student拥有Book,这种方式是不推荐的:

Student *stu = [[Student  alloc] initWithAge:10];

Book *book = [[Book alloc] initWithPrice:3.5];

stu.book = book;

[book release];

[stu release];

是因为点语法调用的setter方法并没有改变book对象的引用计数,所以之后两个对象才会被正常释放并没有内存泄露。下面看一个例子:

//book.h

#import <Foundation/Foundation.h>

@interface Book : NSObject

@property float price;

-(id)initWithPrice:(float)price;

@end

//book.m

#import "Book.h"

@implementation Book

-(id)initWithPrice:(float)price{

if (self == [super init]){

_price = price;

}

return self;

}

-(void)dealloc{

NSLog(@"book:%f 被销毁了", _price);

[super dealloc];

}

@end

//student.h

#import <Foundation/Foundation.h>

#import "Book.h"

@interface Student : NSObject {

//这里需要定义一个成员变量,因为自己写了get和set方法

Book *_book;

}

@property int age;

-(id)initWithAge:(int)age;

-(Book *)book;

-(void)setBook:(Book *)book;

-(void)readBook;

@end

//student.m

#import "Student.h"

@implementation Student

-(id)initWithAge:(int)age{

if (self == [super init]){

_age = age;

}

return self;

}

-(Book *)getBook {

return _book;

}

-(void)setBook:(Book *)book {

//需要先判断,防止重复赋值,同时防止retain一个已释放的野指针

if(_book != book){

//先释放旧的成员变量,OC中即便释放nil也不会出空指针错误

//但是释放一个野指针就会报错

[_book release];

//必须要手动给子对象的引用计数+1

_book = [book retain];

}

}

-(void)readBook{

NSLog(@"当前读的书是:%f", _book.price);

}

-(void)dealloc{

//在类的set方法里retain,类自己就要负责销毁

[_book release];

NSLog(@"student:%i 被销毁了", _age);

//最后调用父类的释放方法

[super dealloc];

}

@end

//main.m

#import <Foundation/Foundation.h>

#import "Student.h"

#import "Book.h"

void test(Student *stu){

Book *book = [[Book alloc] initWithPrice:3.5];

stu.book = book;

//在哪个方法里分配,就在哪个方法里释放

[book release];

stu.book = book;

Book *book2 = [[Book alloc] initWithPrice:4.5];

//经常会重新调用set方法,所以它的内部负责释放先前的book

stu.book = book2;

[book2 release];

}

int main(int argc, const char * argv[])

{

@autoreleasepool {

Student *stu = [[Student  alloc] initWithAge:10];

test(stu);

[stu readBook];

[stu release];

}

return 0;

}

如果一个类持有另一个类,而且在.h文件里使用#import "Book.h"导入的话,会把.h全拷过去,性能会受影响,而且getset等方法也会暴露出去,一般不这么写,习惯在.h文件里使用关键字:@class Book;,只要知道Book是个类就可以了,在.m文件里真正要用到类里的方法时,再使用#import "Book.h"来获取其中的方法。一个典型的错误就是A类improt了B类,B也improt了A类,这样就会死循环而报错。而且,如果有多个文件同时improt了一个头文件,那么一旦这个头文件发生了改变,其他引用它的文件都要重新编译,而@class不存在这个问题。

需要注意的是,如果是继承某个类,就一定要improt头文件,因为这样才能知道它里面定义了什么方法。如果只是定义成员变量或属性,就@class。

假设这时候Student类里有很多对象做属性,那么代码是这样:

#import "Student.h"

#import "Book.h"

#import "Card.h"

@implementation Student

-(void)setBook:(Book *)book{

if (_book != book){

[_book release];

_book = [book retain];

}

}

-(void)setCard:(Card *)card{

if (_card != card){

[_card release];

_card = [card retain];

}

}

-(void)dealloc{

[_book release];

[_card release];

[super dealloc];

}

@end

这些内存管理的set方法太啰嗦了,所以可以不手写这些set方法,而在属性定义的时候这样:

@class Book;

@class Card;

@interface Student : NSObject

@property (retain) Book *book;

@property (retain) Card *card;

@end

这里的retain代表先release旧值,然后retain新值,就不用写内存安全的成员对象的set方法了。所以所有OC对象最好都这么写,但是如果不写,就会生成不管理内存的标准getset方法。

对于常量类型的成员变量,有这样的写法:

@property (assign) int age;

这个assign可以不写,默认就是这样,而且不能写retain,非OC对象不需要管理内存。

Property后面可以带多个参数,用逗号间隔,比如:

@property (nonatomic,retain) Card *card;

这里可以写的属性有三种:

getter的处理:readwrite(默认)/readonly(只生成get方法),

setter的处理:assign(默认,直接赋值)/retain(先释放后ratain)/copy(先释放后copy),

原子性:atomic(默认,给getset方法加锁,线程安全)/nonatomic(禁止多线程保护,性能更好)。通常iphone开发不考虑线程安全。

对于BOOL类型,需要保证get方法变成IsXxxx,需要加上参数:

@property (nonatomic, getter = isRich) BOOL rich;

OC有自动释放池机制,这与java垃圾回收不同,在池子释放时,会对池子里所有的对象调用一次release方法(并不是销毁)。OC对象只要发送一条autorelease消息,OC就会把这个对象添加到释放池当中。所以说autorelease实际是把对象的释放延迟到池子释放了,但是它并不会改变计数器。

最常用的写法是:

@autoreleasepool {

Student *stu = [[[Student alloc]init]autorelease];

}

在OC中有一个潜规则,用来快速创建一个对象的静态方法和类名相同,而且静态方法都自动释放,比如有一个创建Student的静态方法:

@interface Student : NSObject

+(id)student;

@end

实现的时候一定要注意自动释放:

+(id)student {

return [[[Student alloc]init]autorelease];

}

静态方法一般都不需要我们手动来管理内存。

注意点:

在ARC下,只能使用@符号创建一个释放池。

不要把大量循环操作放在释放池下,因为这会导致大量循环内的对象没有被回收,这种情况下应该手动写release代码。

尽量避免对大内存对象使用autorelease,否则会延迟大内存的回收。

ios中很多对象都已经自动释放了,不需要手动再release。

Category(分类)可以动态地给已经存在的类添加方法,类似C#的扩展方法。需要新建一个文件,类型是OCcategory,在Category on当中选择目标类,会生成“类名+分类名”.h和.m两个文件,生成的类名右边括号里就是分类名。需要注意的是这个类的.h文件里必须import原始类,不能@class,原因是要知道原先类里有什么方法。

#import "Student.h"

@interface Student (Test)

-(void)test2;

@end

#import "Student+Test.h"

@implementation Student (Test)

-(void)test2{

NSLog(@"调用了test2方法");

}

@end

#import <Foundation/Foundation.h>

#import "Student.h"

#import "Student+Test.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

Student *stu = [Student student];

[stu test2];

}

return 0;

}

Protocol(协议)类似于C#/java中的接口,可以声明方法,与java不同的是实现类可以只实现一部分方法。在OC中的潜规则是协议名都是“Xxxelegate”。

OC的最根本协议叫NSObject,定义一个协议需要实现这个根本协议,实现协议用尖括号表示,以一个按钮点击监听器做例子:

//Button.h

#import <Foundation/Foundation.h>

//为了让协议用Button做参数,声明这个类

@class Button;

//定义一个协议,实现基础协议,以Delegate结尾

@protocol ButtonDelegate <NSObject>

//定义协议的点击方法,顺便把触发的按钮传进来

-(void)onClick:(Button *)btn;

@end

//定义一个按钮类

@interface Button : NSObject

//定义一个遵循协议的delegate属性,遵循协议用尖括号表示

//相当于在java当中定义一个接口类型的属性

@property (nonatomic, retain) id<ButtonDelegate> delegate;

//按钮有一个模拟的点击方法,用于触发协议中的onClick

-(void)click;

@end

//Buttom.m

//导入按钮的头文件

#import "Button.h"

//按钮的实现

@implementation Button

//为防止内存泄露需要先释放协议

-(void)dealloc{

[_delegate release];

[super dealloc];

}

//按钮点击的模拟方法

-(void)click{

//OC语法:判断代理有没有实现onClick:方法

if ([_delegate respondsToSelector:@selector(onClick:)]){

//调用协议当中的方法,并把sender传进去

[_delegate onClick:self];

}

}

@end

//Buttom.m

//导入按钮的头文件

#import "Button.h"

//按钮的实现

@implementation Button

//为防止内存泄露需要先释放协议

-(void)dealloc{

[_delegate release];

[super dealloc];

}

//按钮点击的模拟方法

-(void)click{

//OC语法:判断代理有没有实现onClick:方法

//这个_delegate是主函数通过set方法赋值进去的

if ([_delegate respondsToSelector:@selector(onClick:)]){

//调用协议当中的方法,并把sender传进去

[_delegate onClick:self];

}

}

@end

//ButtonListener.h

#import <Foundation/Foundation.h>

//因为要用到协议所以提前声明

@protocol ButtonDelegate;

//尖括号表示实现协议

@interface ButtonListener : NSObject <ButtonDelegate>

@end

//ButtonListener.m

//导入监听器自身头文件

#import "ButtonListener.h"

//为使用协议的方法导入协议所在的头文件

#import "Button.h"

//实现的地方就不用尖括号协议名了

@implementation ButtonListener

-(void)onClick:(Button *)btn {

NSLog(@"按钮-%@被点击了", btn);

}

@end

//main.m

#import <Foundation/Foundation.h>

#import "Button.h"

#import "ButtonListener.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

ButtonListener *listener = [[[ButtonListener alloc]init]autorelease];

Button *btn = [[[Button alloc]init]autorelease];

Button *btn2 = [[[Button alloc]init]autorelease];

btn.delegate =listener;

btn2.delegate =listener;

[btn click];

[btn2 click];

}

return 0;

}

通常建议建立一个OCProtocol单独保存协议,只有.h没有.m,因为它不需要实现类。

如果需要同时实现两个协议,则<协议1,协议2>。

如果需要控制协议里的方法必须要实现,则需要在方法的上面加上@required,这样一来,这个标记下的所有方法都必须实现了,但是即便标记了,也可以不实现,编译器不会报错,因为C语言语法弱。选择性实现的用@optional表示,默认是required(废的)。

判断一个类是否实现了协议,有如下方法:

if ([listener conformsToProtocol:@protocol(ButtonDelegate)])

Block封装了一段代码,可以在任何时候执行,类似于函数指针,也类似C#的Func和Action,它用尖括号定义,可以做参数注入lambda,也可以做回调,比如:

int (^Sum)(int,int) = ^(int a, int b) {

return a + b;

};

int a = Sum(10,11);

在block里是可以使用花括号外的变量的,但是不能修改它,类似java在外部加一个final,除非加一个关键字__block就能改变了,比如:

void test() {

__block int c = 1;

int (^Sum)(int,int) = ^(int a, int b) {

c = 10;

return a + b + c;

};

NSLog(@"%i", Sum(1,2));

}

另外可以提前声明block的类型:

typedef int (^MySum) (int,int);

这样就可以用这个类型来定义block了:

MySum sum = ^(int a, int b) {

return a+b;

};

下面是一个block作为监听器属性的例子,可以理解为从调用层set一个lambda表达式进去:

//main.m

#import <Foundation/Foundation.h>

#import "Button.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

Button *btn = [[[Button alloc] init] autorelease];

btn.block = ^(Button *btn) {

NSLog(@"按钮-%@被点击了", btn);

};

[btn click];

}

return 0;

}

//buttom.h

#import <Foundation/Foundation.h>

@class Button;

//定义一个block的类型

typedef void (^ButtonBlock) (Button *);

@interface Button : NSObject

//定义一个block的属性

@property (nonatomic, assign) ButtonBlock block;

-(void)click;

@end

//Button.m

#import "Button.h"

@implementation Button

-(void)click{

_block(self);

}

@end

常用结构体

typedef struct _NSRange {

NSUInteger location;

NSUInteger length;

} NSRange;

表示一个范围,比如字符串中某个子串的位置范围。

结构体可以直接赋值:

NSRange range = {.location = 7, .length = 3};

也可以使用函数(常用):

NSRange ranges = NSMakeRange(7,3);

可以用函数把NSRange转成字符串:

NSStringFromRange(range)

struct CGPoint {

CGFloat x;

CGFloat y;

};

typedef struct CGPoint CGPoint;

typedef CGPoint NSPoint;

NSPoint表示一个点。

构建函数:

NSMakePoint(10,9);

CGPointMake(10,9);(常用)

快速打印使用函数:

NSStringFromPoint(point)

struct CGSize {

CGFloat width;

CGFloat height;

};

typedef struct CGSize CGSize;

typedef CGSize NSSize;

表示宽度和高度,同样有如下方法:

NSMakeSize(10, 8);

CGSizeMake(10, 8);(常用)

NSStringFromSize(size);

struct CGRect {

CGPoint origin;

CGSize size;

};

typedef struct CGRect CGRect;

typedef CGRect NSRect;

存储位置和尺寸,也就是一个矩形范围。

创建:

NSMakeRect(10, 10, 80, 80);

CGRectMake(10, 10, 80, 80);(常用)

打印:

NSStringFromRect(rect)

字符串:

//字符串常量,不用管内存

NSString *str1 = @"A String";

//常规方法

NSString *str2 = [[NSString alloc] init];

str2 = @"A String";

[str2 release];

//构造方法

NSString *str3 = [[NSString alloc] initWithString:@"A String"];

[str3 release];

//对应的静态方法,不需要管理内存(推荐使用静态方法)

str3 = [NSString stringWithString:@"A String"];

//转化C语言的字符串

NSString *str4 = [[NSString alloc] initWithUTF8String:"A String"];

[str4 release];

str4 = [NSString stringWithUTF8String:"A String"];

//格式化创建

NSString *str5 = [[NSString alloc] initWithFormat:@"My age is %i and height is %.2f", 19, 1.55f];

[str5 release];

str5 = [NSString stringWithFormat:@"My age is %i and height is %.2f", 19, 1.55f];

可以从文件里读字符串:

int main(int argc, const char * argv[])

{

@autoreleasepool {

NSError *error;

NSString *path = @"/Users/mac/Desktop/1.txt";

NSString *str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];

if (error == nil) {

NSLog(@"%@", str);

} else {

NSLog(@"%@", error);

}

}

return 0;

}

这个方法的要求一个NSError**类型的参数,也就是需要一个指向指针的指针,所以就需要传一个指针的地址过去,因为调用函数时产生了一个临时的指针型变量,所以对这个临时变量修改,是不会反映到外部变量的。

也可以通过URL读取:

NSURL *url = [NSURL URLWithString:@"http://www.badu.com"];

NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

如果需要定义一个方法改变字符串,应该这样写:

void change(NSString **str){

*str = @"456";

}

int main(int argc, const char * argv[])

{

@autoreleasepool {

NSString *str = @"123";

change(&str);

NSLog(@"%@", str);

}

return 0;

}

将字符串写入文件如下:

NSString *str = @"123456";

NSString *path = @"/Users/mac/Desktop/1.txt";

NSError *error;

[str writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error];

第二个参数是原子性,如果是yes,则会将字符串写入一个临时文件,全都写完之后剪切到目标文件,如果写临时文件中途报错,临时文件就会被删除,更加安全。如果是非原子性,则直接写到目标文件。

其他常用方法:

NSString *str = @"guangDong";

NSLog(@"%@",[str uppercaseString]);

NSLog(@"%@",[str lowercaseString]);

//首字母变大写,其他字母全变小写

NSLog(@"%@",[str capitalizedString]);

//比较字符串内容

BOOL result = [@"abc" isEqualToString:@"ABC"];

//比大小,右边的大是升序返回1,左边的大是降序返回0

NSComparisonResult result1 = [@"abc" compare:@"ABC"];

//忽略大小写比较

NSComparisonResult result2 = [@"abc" caseInsensitiveCompare:@"ABC"];

NSString *str = @"123456.txt";

//startWith

BOOL result1 = [str hasPrefix:@"123"];

//endWith

BOOL result2 = [str hasSuffix:@"txt"];

//搜索

NSRange range = [str rangeOfString:@"345"];

if (range.location == NSNotFound) {

//没有找到

}

//截取

[str substringFromIndex:3];

[str substringToIndex:5];

[str substringWithRange:NSMakeRange(2, 4)];

//split

NSArray *arr = [str componentsSeparatedByString:@","];

return 0;

}

将数组拼接成路径pathWithComponents

将路径分解成数组pathComponents

是否是绝对路径,(本质是判断左边是不是/) isAbsolutePath

返回最后一个目录lastPathComponent

获取除了最后一个目录之外的路径stringByDeletingLastPathComponent

在最后面拼接一个路径stringByAppendingPathComponent

获取扩展名pathExtension

删掉扩展名stringByDeletingPathExtension

拼接扩展名stringByAppendingPathExtension

NSString *str = @"100";

//转int

int a = [str intValue];

//算字数

int len = [str length];

//取字符

unichar c = [str characterAtIndex:0];

//返回c语言的字符串

char *s = [str UTF8String];

除了NSString,还有一个NSMutableString是可变字符串,是NSString的子类。

NSMutableString *str = [[NSMutableString alloc] initWithCapacity:8];

[str setString:@"1234"];

[str appendString:@"567890"];

[str replaceCharactersInRange:[str rangeOfString:@"456"] withString:@"xxx"];

[str insertString:@"yyy" atIndex:6];

[str deleteCharactersInRange:[str rangeOfString:@"xxyy"]];

NSLog(@"%@", str);

[str release];

NSArray是不可变数组,可以放任何OC对象,创建方法:

//创建一个空数组(不能再往里加东西了)

NSArray *array = [NSArray array];

//创建一个有元素的数组,只允许装OC对象,也不能装nil,nil表示结束

NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",nil];

常用方法:

//获取元素个数,实际是个get方法

int count = array1.count;

int count1 = [array1 count];

//判断元素存在

if ([array1 containsObject:@"a"]) { }

//获取最后一个元素

NSString *last = [array1 lastObject];

//根据位置获取

NSString *one = [array1 objectAtIndex:1];

//获取元素位置

int index = [array1 indexOfObject:@"b"];

当把一个对象加入数组时,对象的计数器会+1,当数组被销毁时,会把里面每一个元素的计数器-1,所以不用管数组内部对象的内存问题。

NSArray可以让每个对象调用一个方法,但是有一定的局限性,最多传递一个参数:

[array makeObjectsPerformSelector:@selector(test)];

[array makeObjectsPerformSelector:@selector(test2:) withObject:@"123"];

OC类似js可以遍历数组:

for (id obj in array) {

}

OC还有类似C#中ForEach执行lambda的方法:

[array enumerateObjectsUsingBlock:

^(id obj, NSUInteger idx, BOOL *stop) {

NSLog(@"%@-%zi", obj, idx);

if (idx == 1) {

*stop = YES;   //停止遍历

}

}];

另外OC还可以通过objectEnumerator方法得到迭代器,可以使用nextObject等方法。

数组可以拼接:

//添加元素生成新的数组

NSArray *array2 = [array arrayByAddingObject:@"3"];

//添加另一个数组

NSArray *array3 = [array arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"3",@"4", nil]];

//截取子元素

NSArray *array4 = [array3 subarrayWithRange:NSMakeRange(2, 3)];

//用分隔符拼接数组每个元素成字符串

NSString *str = [array3 componentsJoinedByString:@","];

数组可以排序:

//用一个指定的比较方法进行排序

NSArray *array2 = [array sortedArrayUsingSelector:@selector(compare:)];

//用block设置比较方件进行排序

NSArray *array3 = [array sortedArrayUsingComparator:^NSComparisonResult(Student *obj1, Student * obj2) {

//比较算法

}];

//使用排序描述器进行复杂排序,这里读的是属性

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];

NSSortDescriptor *sortDescriptor2 = [NSSortDescriptor sortDescriptorWithKey:@"book.name" ascending:YES];

NSArray *descs = [NSArray arrayWithObjects:sortDescriptor,sortDescriptor2,nil];

NSArray *array4 = [array sortedArrayUsingDescriptors:descs];

与数组对应的还有可变数组NSMutableArray,是NSArray的子类,可以使用addObject方法添加对象,也有对应的删除方法等。当对可变数组添加或删除对象时,会对对应对象进行retain或release操作。

和数组类似,OC有NSDictionary来存放key-value对,有如下常用方法:

//创建

NSDictionary *dic = [NSDictionary dictionaryWithObject:@"v" forKey:@"k"];

//最常用的

NSDictionary *dic2 = [NSDictionary dictionaryWithObjectsAndKeys:@"v1",@"k1",@"v2",@"k2", nil];

NSArray *objects = [NSArray arrayWithObjects:@"v1",@"v2",nil];

NSArray *keys = [NSArray arrayWithObjects:@"k1",@"k2",nil];

NSDictionary *dic3 = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

取值方法:

id value = [dic objectForKey:@"k1"];

另外有allKeys,allValues方法,但是词典并不是有序的。

多个key可以对应一个value,可以使用allKeysForObject来获取所有的key。

//传统遍历

for (id key in dic) {

id value = [dic objectForKey:key];

NSlog(@"%@", key);

}

//key迭代器

NSEnumerator *enumer = [dic keyEnumerator];

id key = nil;

while (key = [enumer nextObject]){

NSlog(@"%@", key);

}

//object迭代器

//[dic objectEnumerator]

//block迭代器

[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

//...

}];

对于词典的内存管理,不论是当做key还是value放入词典,都会把计数器+1,做为key的对象要实现NSCopying协议。在字典释放时,会自动把里面的东西-1。

词典的子类是NSMutableDictionary是可变词典,有add方法:[dic setObject:@"v4" forKey:@"k4"];,也有相应的移除方法removeObjectForKey。

NSNumber是对非OC对象的包装器,用于把int等类型的变量打包成对象放入集合,但是不支持自动打包解包。

NSNumber *number = [NSNumber numberWithInt:10];

int num = [number intValue];

但是NSNumber不能包装结构体,所以需要用NSValue,它是NSNumber的父类,可以包装任何值,比如:

//包装系统自带的结构体

CGPoint point = CGPointMake(10, 10);

NSValue *value= [NSValue valueWithPoint:point];

//取值

CGPoint point1 = [value pointValue];

//包装自定义的结构体

char *type = @encode(CGPoint);

NSValue *value1 = [NSValue value:&point withObjCType:type];

//取值

CGPoint point2;

[value1 getValue:&point2];

NSNull用来表示空值,和nil不同,NSNull是一个OC对象,也有计数器和内存管理,这个NSNull对象是全局单例的,它只有一个方法:

NSNull *n = [NSNull null];

NSDate表示时间,返回当前时间是:

NSDate *date = [NSDate date];

从当前时间再增加一些秒数返回:

NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5];

获取时间间隔:

NSTimeInterval interval = [date1 timeIntervalSinceDate:date2];

NSObject是所有类的父类,有很多常用方法:

判断一个类是不是一个类(的子类),第二个参数class是一个指向结构体的指针:

[date1 isKindOfClass:[NSDate class]];

相比之下,isMemberOfClass方法的范围更小,只能判断是不是这个类,而不能判断是不是子类。

conformsToProtocol方法判断是否实现了某个协议。

间接调用方法:

[stu performSelector:@selector(test)];

这种间接调用可以传OC对象类型的参数,但是最多支持到两个参数:

[stu performSelector:@selector(test2) withObject:@"abc"];

OC支持反射创建类的对象:

Student *stu = [[NSClassFromString(@"Student") alloc]init];

也可以把一个类型变成字符串:

NSString *str = NSStringFromClass([Student class]);

根据字符串调用方法:

[stu performSelector:NSSelectorFromString(@"test")];

将方法名转为字符串:

NSString *str2 = NSStringFromSelector(selector);

copy让一个对象产生一个副本,修改副本不会修改原先的对象,需要支持copy的类需要实现NSCopying/NSMutableCopying协议,这两个协议分别用来创建不可变/可变副本。使用mutableCopy语法会返回新对象,但是对NSString对象使用copy语法会返回对象本身,因为它本身就是不能改的。copy是浅拷贝,mutableCopy是深拷贝。如果反过来用copy复制一个NSMutableString的话,会返回一个NSString,是深拷贝。也就是说,只有不可变拷贝为不可变时,才是浅拷贝。

一个类的属性参数可以设置retain让set方法自动管理内存,比如:

@property (nonatomic, retain) NSString *name;

这里retain可以改成copy,则会在set方法内copy,而不是retain,如果是浅拷贝,那么就相当于retain了。这时set方法就会先release旧对象,之后copy新对象。

如果一个对象里的属性retain了一个外部对象作为成员,那么外部的改变就会影响这个对象的属性,为了防止这种影响,需要使用copy策略,如果是NSString类型的成员,最常用的就是copy策略。

让自己的类可以copy的话,需要实现NSCopying协议,实现copyWithZone方法,zone就是新的存储空间,这里创建的副本不要求释放。

@interface Student : NSObject <NSCopying>

@property (nonatomic, copy) NSString *name;

- (id)copyWithZone:(NSZone *)zone;

@end

#import "Student.h"

@implementation Student

- (id)copyWithZone:(NSZone *)zone{

Student *copy = [[Student allocWithZone:zone] init];

copy.name = self.name;  //拷贝成员

return copy;

}

@end

如果说子类也需要copy的话,则需要复写父类的copy方法,在其中调用父类的copy方法之后给子类成员赋值。完整代码示例如下:

//GoodStudent.m

#import "GoodStudent.h"

@implementation GoodStudent

+(id)goodStudentWithAge:(int)age name:(NSString *)name {

GoodStudent *good = [GoodStudent studentWithName:name];

good.age = age;

return good;

}

-(id)copyWithZone:(NSZone *)zone {

GoodStudent *copy = [super copyWithZone:zone];

copy.age = self.age;

return copy;

}

@end

//GoodStudent.m

#import "GoodStudent.h"

@implementation GoodStudent

+(id)goodStudentWithAge:(int)age name:(NSString *)name {

GoodStudent *good = [GoodStudent studentWithName:name];

good.age = age;

return good;

}

-(id)copyWithZone:(NSZone *)zone {

GoodStudent *copy = [super copyWithZone:zone];

copy.age = self.age;

return copy;

}

@end

//GoodStudent.h

#import <Foundation/Foundation.h>

#import "Student.h"

@interface GoodStudent : Student

@property (nonatomic, assign) int age;

+(id)goodStudentWithAge:(int)age name:(NSString *)name;

@end

//GoodStudent.m

#import "GoodStudent.h"

@implementation GoodStudent

+(id)goodStudentWithAge:(int)age name:(NSString *)name {

GoodStudent *good = [GoodStudent studentWithName:name];

good.age = age;

return good;

}

-(id)copyWithZone:(NSZone *)zone {

GoodStudent *copy = [super copyWithZone:zone];

copy.age = self.age;

return copy;

}

@end

KVC

是一种无须知道类型的间接属性访问方式,解除了赋值取值操作与类型数据结构的耦合,

为了防止错误尽量用keyPath方法就好了。

[p setValue:@10 forKey:@"age"];

NSNumber *age = [p valueForKey:@"age"];

[p setValue:@"100" forKeyPath:@"card.no"];

NSString *cardno = [p valueForKeyPath:@"card.no"];

KVC与词典有容易混淆的地方:

NSMutableDictionary *dic = nil;

//词典语法

[dic setObject:<#(id)#> forKey:<#(id<NSCopying>)#>];

//kvc语法

[dic setValue:<#(id)#> forKey:<#(NSString *)#>];

其他用法如:

通过路径快速抽取一个book数组中的所有价格(类似C#的linq)

NSMutableArray *prices = [books valueForKeyPath:@"price"];

KVO是用来监听对象值改变的监听器机制,首先定义监听器:

#import "PersonObserver.h"

@implementation PersonObserver

//当监听的某个属性发生改变时调用

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{

}

@end

主函数:

@autoreleasepool {

Person *p = [[Person alloc]init];

PersonObserver *po = [[PersonObserver alloc]init];

[p addObserver:po forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

//...

[p removeObserver:po forKeyPath:@"name"];

}

这段代码定义了p对象并且给p的name属性设定了一个监听器,第三个参数是用来传递给监听器的change参数,能获取到改变前后的值,context是可以传入的自定义对象。

监听器需要及时释放(否则会提示警告信息)。

原文:oc总结

时间: 2024-10-16 03:01:47

oc总结 --oc基础语法相关知识的相关文章

HTML入门基础教程相关知识

HTML入门基础教程 html是什么,什么是html通俗解答: html是hypertext markup language的缩写,即超文本标记语言.html是用于创建可从一个平台移植到另一平台的超文本文档的一种简单标记语言,经常用来创建web页面.html文件是带有格式标识符和超文本链接的内嵌代码的ascii 文本文件——html结构了解. html文本是由 html命令组成的描述性文本,html 命令可以说明文字. 图形.动画.声音.表格.链接等. html网页结构包括头部 (head).主

黑马day05 jsp语法相关知识

1.jsp技术 jsp是sun提供动态web资源开发技术.为了解决在Servlet中拼写html内容css.js内容十分不方便的问题,sun提供了这样一门技术.如果说Servlet是在java中嵌套HTML,则jsp就是在HTML中嵌套java代码,从而十分便于组织html页面 jsp页面在第一次被访问到时会被jsp翻译引擎翻译成一个Servlet,从此对这个jsp页面的访问都是由这个Servlet执行后进行输出 2.jsp语法 (1)JSP模版元素 :jsp页面中书写的HTML内容称作JSP的

1.JAVA基础——语法相关

1.1 8种基本数据类型 数值型 整数类型:byte(1字节),short(2字节),int(4字节),long(8字节) 浮点类型:float(4字节),double(8字节) 非数值型 字符型:char(2字节) 布尔型:boolean ①关于字节(Byte)和位(bit):数据存储以字节(Byte)为单位,数据传输大多以位(bit)为单位,在二进制中,一个"位"即表示一个0或1,每8个bit组成一个Byte,bit是最小一级的信息单位. ②关于补码:计算器存储二进制数值是以补码形

HTML基础--浏览器相关知识(html,doctype,字符编码)

字符集就是:字符(Character)是各种文字和符号的总称,包括各国家文字.标点符号.图形符号.数字等.字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集.GB2312字符集.BIG5字符集. GB18030字符集.Unicode字符集等.为了准确各种字符集文字,就需要字符编码来管理这些字符集中的字符,来使计算机能够准确的处理字符集. 常见的字符编码有:utf-8,ascii码,gb2312等. 网页中的乱码:

C和OC的基础语法(易混淆的概念知识)

List 0. 深复制与浅复制, NSObject万能指针.id指针.instancetype区别,单例import.include.@class的区别 strong 与 weak 区别 #define  和  typedef的区别, static 与  extern 区别,@required与@optional 区 别,@private.@protected .@public.@package区别 变量的命名规则以及规范(4规则,2规范) 数据类型转换 printf与scanf,自增自减,逻辑

OC - 2.OC基础知识介绍

一.基础语法 1> OC语言和C语言 C语言是面向过程的语言,OC语言是面向对象的语言 OC语言继承了C语言,并增加了面向对象的思想 以下内容只介绍OC语言与C语言的不同之处 2> 关键字 以字母或下划线开头 3> 字符串 OC语言有自己特有的字符串,,区别于C语言字符串 4> BOOl类型 只有YES和NO两种取值 本质上是char类型,但理解为整型 BOOL类型是由 条件编译机制引进的 5> #import与#include 都是将文件内容拷贝到指令出现的代码行 系统文件

【OC基础语法考试】

OC基础语法已经全部学完,但是这些知识只是最基础的,还有很多高级知识,这个可能需要后面慢慢的去学习才能体会到.接下来我会总结前面的OC基础语法,如果大家发现有什么不正确的地方,请指正,小弟是新生,多请OC老鸟来喷~~ 试题如下: 1.将包含以下三个字符串@“1hello“,@“2word“,@”3good“的数组改写为字典,数字部分作为key,英文部分作为value.(15) 2.建立一个数组对象,使用3个不同的方法,使其引用计数增加为4,输出这个值,然后保证内存能够正确释放.(15) 3.使用

015-OC基础语法-OC笔记

学习目标 1.[了解]Objective-C语言简介 2.[掌握]第一个OC程序 3.[掌握]OC中的字符串 4.[熟悉]OC中的一些玩意 5.[了解]面向过程与面向对象 6.[掌握]类的声明和实现 7.[掌握]类的方法 一.Objective-C语言简介 Objective-C简称Obj-C或者OC,Objective的意思就是面向对象的.OC其实就是在C语言的基础之上,封装了一层面向对象的语法,他并不是一门全新的编程语言.OC是Mac OS X系统和iOS系统的主要开发编程语言,当然马上要被

Ajax基础知识 浅析(含php基础语法知识)

1.php基础语法    后缀名为.php的文件 (1) echo   向页面中输入字符串  <?php    所有php相关代码都要写在<?php ?>这个标签之中 echo "<div>Hello World!</div>"; ?> (2) $  变量声明  如果只声明不赋值,会报错 <?php $num=123; echo $num; echo "<div>编号为:".$num."&l