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總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM