objective-c 文法快速過(4),objective-c文法
oc 裡的字串
字串的快速建立(最簡單的方法)
NSStirng *str = @“Hello”;//oc的字串都是@“”形式的
oc的字串也是類的對象,是NSString類的對象,建立沒有那麼麻煩不用[ ],使用物件導向的思想來操縱字串。
char *name = "xxxxx";//c風格字串
oc使用 %@ 輸出字串,不是%s
NSString *name = @”dashuai”;NSLog(@“我的名字是%@”, name);
c 風格字串輸出解析的用法是%s
NSLog(@"%s", name);//%s是c風格的格式化解析
OC字串的另一方式,Foundation 架構裡的 NSString 類
NSString *newStr = [NSString stringWithFormat:@"My age is %d and no is %d and name is %@", age, no, name];NSLog(@"---- %ld", [newStr length]);
length方法,返回unsigned long類型
NSString *name = @"哈哈達到";// length方法算的是字數總數,包含的文字的數目!不是求字元個數!! int size = [name length];
不懂就去查詢文檔!百度!
// main.m#import <Foundation/Foundation.h>/* NSString : 不可變字串 NSMutableString : 可變字串 */int main(){ NSMutableString *s1 = [NSMutableString stringWithFormat:@"my age is 10"]; // 拼接內容到s1的後面 [s1 appendString:@" 11 12"]; // 擷取is的範圍 NSRange range = [s1 rangeOfString:@"is"]; [s1 deleteCharactersInRange:range]; NSString *s2 = [NSString stringWithFormat:@"age is 10"]; //建立字串,拼接好的字串到建立的字串裡,但是原來的字串沒有變!兩個方法不一樣。 NSString *s3 = [s2 stringByAppendingString:@" 11 12"]; NSLog(@"s1=%@, s2=%@", s1, s2); return 0;}void stringExport(){ // 字串的匯出 [@"Jack\nJack" writeToFile:@"/Users/apple/Desktop/my.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil]; NSString *str = @"4234234"; NSURL *url = [NSURL fileURLWithPath:@"/Users/apple/Desktop/my2.txt"]; [str writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:nil];}void stringCreate(){ /* 1.字串的建立 */ NSString *s1 = @"jack"; //NSString *s2 = [[NSString alloc] initWithString:@"jack"];這種方法不推薦! NSString *s3 = [[NSString alloc] initWithFormat:@"age is %d", 10]; // C字串 --> OC字串 NSString *s4 = [[NSString alloc] initWithUTF8String:"jack"]; // OC字串 --> C字串 const char *cs = [s4 UTF8String]; // NSUTF8StringEncoding 用到中文就可以用這種編碼 NSString *s5 = [[NSString alloc] initWithContentsOfFile:@"/Users/apple/Desktop/1.txt" encoding:NSUTF8StringEncoding error:nil]; // URL : 資源路徑 // 協議頭://路徑 // 本地檔案 file:// // ftp:// // 網頁資源 http://weibo.com/a.png // http://www.baidu.com // NSURL *url = [[NSURL alloc] initWithString:@"file:///Users/apple/Desktop/1.txt"]; NSURL *url = [NSURL fileURLWithPath:@"/Users/apple/Desktop/1.txt"]; NSString *s6 = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSLog(@"s6=\n%@", s6); /* 一般都會有一個類方法跟對象方法配對 [NSURL URLWithString:<#(NSString *)#>]; [NSString stringWithFormat:@""]; [NSString stringWithContentsOfFile:<#(NSString *)#> encoding:<#(NSStringEncoding)#> error:<#(NSError *__autoreleasing *)#>]; */}
OC的 set 和 get 方法
類的成員變數盡量不要@public,保持資料的隱蔽性和封裝性,使用對象直接存取類的成員變數也不推薦,因為這樣類的成員變數可以被隨意的修改,不安全,而是應該間接的調用。並且直接調用會讓別人一下子就看到了類的內部結構和資訊,失去了封裝的意義。
set方法
1) 作用:用來設定成員變數,可以在方法裡面過濾掉一些不合理的值
2) 命名規範:
- 方法名都是以set開頭,而且後面跟上成員變數名,成員變數名的首字母必須大寫
- 一定接受參數,且形參名稱不要跟成員變數同名
- 傳回值void
get方法
1) 作用:返回對象內部的成員變數
2) 命名規範:get方法的名稱一般就跟成員變數同名
/*成員變數的命名規範:一定要以底線 _ 開頭@interface Student : NSObject{ 1.讓成員變數和get方法的名稱區分開 2.可以跟局部變數區分開,一看到底線開頭的變數,一般都是成員變數 */ int _no; Sex _sex;}// sex的set和get方法- (void)setSex:(Sex)sex;- (Sex)sex;// no的set和get方法- (void)setNo:(int)no;- (int)no;@end
oc特有的文法—相容c系列的點文法
可以使用點文法來代替oc古怪的對象調用的形式。這是oc特有的讓其他(主要是習慣了 c 系列程式設計語言的程式員,比如 c,c++,java等)程式員快速看懂的一個做法。
// 點文法的本質還是方法調用 p.age = 10; // [p setAge:10];
這裡還是古怪,到底是get還是set方法,要看是給它賦值了(set),還是被賦值了(get)。還是和java或c++的點文法區分。
oc 中類的成員變數的作用於
@public : 在任何地方都能直接存取對象的成員變數
@private : 只能在當前類的對象方法中直接存取。私人成員只能在本類被直接存取,子類不可見,只能是set和get方法訪問(@implementation中預設是@private,因為一般不會包含實現檔案,都是包含標頭檔,故實現檔案預設是私人的)
@protected : 可以在當前類及其子類的對象方法中直接存取 (@interface中預設就是@protected)
@package : 只要處在同一個架構中,就能直接存取對象的成員變數,不常用
@interface和@implementation中不能聲明同名的成員變數,同名衝突,很好理解
Xcode的一個特性@property和@synthesize
sɪnθɪˌsaɪz v綜合合成;(通過化學或生物)合成;(音響)合成
@property
自動產生set和get方法,類似java的eclipse的 setter 和 getter選項
#import <Foundation/Foundation.h>@interface Person : NSObject{ int _age; int _height; double _weight; NSString *_name;}// @property:只能用在聲明裡,可以自動產生某個成員變數的setter和getter聲明,並且和java還不一樣,xcode把自動的聲明隱藏,不可見。這是編譯器的特性!下面這一句,代表注釋的那兩句。@property int age;//- (void)setAge:(int)age;//- (int)age;@property int height;//先產生set再get//- (void)setHeight:(int)height;//- (int)height;- (void)test;@property double weight;//如果成員變數類型一樣,可以合并在一起寫,但是實際開發不這樣,分開比較好@property NSString *name;@end
注意:@property裡的變數age如果沒有被提前聲明,那麼也是可以啟動並執行,編譯器會自動產生_age成員變數,xcode4.4版本之後有的。這樣產生的成員變數,在類聲明裡是@private。若類聲明裡存此成員變數,如果@property還是那麼寫,就不會再自動產生。
@synthesize
唯寫在實現裡。可以自動去類聲明裡找@property,然後根據它去產生類的成員變數的setter和getter方法的實現,並且會訪問這個成員變數
@synthesize age = _age;
如果這樣寫,預設會訪問age這個成員變數,如果沒有age,就會自動產生@private類型的age變數,都是自動存在類聲明裡。
注意:xcode4.4之後,為了讓程式員把精力放在商務邏輯上,而不是無謂的垃圾代碼的書寫上,xcode變得越來越方便,做的東西也越多,現在可以省略類的成員變數的聲明,直接@property就可以自動產生變數和配套的set與get方法的聲明。同樣,4.4之後,類的實現裡,@synthesize也不用寫了。也就是說,set和get的實現不用寫。
意味著,現在的@property 聲明,類聲明裡的@property預設做兩個事情,即產生set和get的聲明,也產生set和get的實現。當然,如果類聲明裡沒有變數聲明,那麼還要做第三個事情,自動產生私人的成員變數。
Xcode原則就是:沒有,我替你產生,有我就直接用。
這裡就可以解釋為什麼蘋果建議類的成員變數前面加底線?
因為@property 的新特性:自從 xcode4.4之後,@property 就獨攬了@synthesize 的功能,也就說,@proeprty 可以同時產生成員變數的 set,get 方法的聲明和實現,兩個都能。預設的情況下,set 和 get 方法的實現,會去訪問底線的成員變數,故要求類的聲明裡使用底線的變數命名方式命名成員變數。說到底:就是因為新特性的@property自動產生set,get方法的聲明和實現,預設訪問的都是前面帶底線的成員變數。
oc 裡的 id 類型(萬能指標)
id 是一種類型
類型不能做變數名,好比 int int;是錯誤的,一個道理。
Id 是萬能指標
能指向(操縱)任何的 oc 對象,而且不能加*,唯寫id就代表指標(因為 id內部已經包含*,見官方文檔)。
Person *p = [Person new];//傳統的操作人的對象,人類型指標p 指向人的對象
萬能指標,id能指向 or 操作任何OC對象,只適合用在 oc 對象上
id d = [Person new];//id 指向人的對象
id 的缺點:
如果同樣是指向(調用)類不存在的對象方法,那麼編譯器直接報錯!不會弱文法了
千萬別加*
oc 的構造方法和重寫
構造方法:用來初始化對象的方法,oc裡是個對象方法,-開頭 -init
物件導向的語言,都包含的東西---構造方法,用和 c++類似的new 這個類方法建立對象,其實不好。
// Person *p = [Person new];不推薦這樣建立對象
new 其實是類方法,是 NSObject 類的方法,可以建立對象,分配記憶體,但是事實上,我們不常用,因為太死板,做的事情很單調,就是完整的建立一可用對象。
完整地建立一個可用的對象其實分兩步:
1.分配儲存空間 +alloc
2.初始化 -init
new 方法做這兩個事情,new 方法內部,調用兩個方法(一個類方法,一個對象方法)完成這個步驟,細節為:
1.調用+alloc分配儲存空間
Person *p1 = [Person alloc];//此時對象不可用,沒有初始化,返回 id(oc 對象)
2.調用-init進行初始化
Person *p2 = [p1 init];反回 oc 對象(已經初始化之後)本身
說明 new 是兩個方法的聯合,開發不常用,比較死板,一般分兩步做,這樣後果是方便選擇。因為初始化不一定必須是 init對象方法初始化。。
兩步合并的簡單寫法:掌握,開發常用這個寫法,從此不用 new 了。
Person *p4 = [[Person alloc] init];
Oc的構造方法就是-init 方法,用來初始化對象的方法。對象方法,每個對象內部的成員變數預設都初始化為0,
建立 person 對象,保證她的_age 預設都是10,不是0,怎麼做?
需要重寫 –init方法,覆蓋父類 –init方法
重寫構造方法的目的:
為了讓對象建立出來,成員變數就會有一些固定的值
重寫構造方法的注意點
1.先調用父類的構造方法([super init])
2.再進行子類內部成員變數的初始化,和 c++類似。
Person.h#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@endPerson.m#import "Person.h"@implementation Person// 重寫-init方法,其實 id 有些類似 c++的模板的應用。//- (id)init//{// 1、一定要先調用super的init方法:先初始化父類中聲明的一些成員變數和其他屬性 self = [super init]; // 返回的是當前oc對象,一定用 self接受,這裡需要好好研究,Crowdsourced Security Testing道。// 2.如果父類對象初始化成功,才有必要進行接下來的初始化// if (self != nil)// { // _age = 10;// }// // 3.返回一個已經初始化完畢的oc對象// return self;//}
//綜合的寫法,熟練之後如下這樣寫:- (id)init{ if ( self = [super init] ) { _age = 10; } return self;}@end
要求:
學生類的學生對象,初始化完畢之後,年齡都是10,學號都是1.還是重寫構造方法-init
Student.h繼承 person 類#import "Person.h"@interface Student : Person@property int no;@end
Student.m#import "Student.h"@implementation Student// 學生對象初始化完畢後,年齡就是10,學號就是1- (id)init{ if ( self = [super init] ) { _no = 1; } return self;}@end
main.h#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(){ // 每個stu對象建立出來,他的_age都是10,學生學號都是1,isa 指向本身每個 p4對象建立出來,她的 _age 都是10,isa 指向本身類 Person *p4 = [[Person alloc] init]; Student *stu = [[Student alloc] init]; return 0;}
student 繼承了 person ,而 person繼承了 NSObject,比如 student 對象內部有三個成員變數,分別為: student 自己的_no,和繼承自父類 person 類的_age,還有 person 最開始就繼承的父類NSObject 的 isa指標,這個三個成員變數,都是 student 對象 stu 包含的!
因為student 類 建立 student 對象的時候,先進入 student 類尋找-init,(因為 oc 裡規定,如果父類和子類有同名方法,子類對象優先調用子類同名方法),然後 在 student 類裡發現- (id)init方法,之後又發現:
{ if ( self = [super init] )這句話,那麼編譯器會率先進入到父類 person,因為 super 的作用 { _no = 1; } return self;}
然後先調用直接父類 person 的 –init,而 person 的-init為:
- (id)init{ if ( self = [super init] )還是執行到此,我們發現調試就進不去了,因為 super 調用的是 NSObject,蘋果是不開源的,但是事實還是進去偷著執行 NSobject 的-init 方法,初始化isa 指標, 把 isa 初始化為指向student 類本身,記住是當前的類,初始化完畢,程式執行回到 person 類,繼續向下執行_age=10。(同時student 的成員變數 age 變為10) { _age = 10; } return self;最後返回的 self 指向的是 當前student 的對象}
當person的-init 調用完畢,最後才回到 子類student 類,繼續執行 student 的-init, 執行_no=1,繼承來的 isa 初始化為指向本身 student 類,這才初始化完畢。
person 對象初始化的時候,同理,先在本類尋找初始化方法,發現 super 語句,然後再去父類 NSObject 類 裡尋找-init方法,(這裡先記住 self=[super init]的寫法,下來再深入),NSObject 做一件事,那就是初始化 isa,把 isa 指向 person 類本身,(指向當前類,這個類是畢源的,看不到內部代碼)然後回到 person類,_age=10。執行完畢。
再看一看 student 對象,建立的時候,先初始化,同理在本類,然後 發現 super,那麼去父類 person 執行-init,還是發現 super,還是去 NSObject執行-init,把 isa 指向 student 類本身,(當前類不是 person,是 student 了)然後回到 person,_age=10.回到student,_no=1,執行完畢,再回main 函數,初始化,完畢。
oc構造方法的自訂
oc自訂構造方法的規範
1.一定是對象方法, 故一定以 - 開頭
2.傳回值一般是id類型
3.方法名一般以initWith開頭
這讓我想起了,c++建構函式的含參構造和預設構造,和不帶參數的構造,意義是類似的。其實就是帶參構造。和 c++大同小異。
Person.h#import <Foundation/Foundation.h>@interface Person : NSObject@property NSString *name;@property int age;- (id)initWithName:(NSString *)name;- (id)initWithAge:(int)age;- (id)initWithName:(NSString *)name andAge:(int)age;@endPerson.m#import "Person.h"@implementation Person- (id)initWithName:(NSString *)name{ if ( self = [super init] )固定寫法,記住 { _name = name; } return self;}- (id)initWithAge:(int)age{ if ( self = [super init] ) { _age = age; } return self;}- (id)initWithName:(NSString *)name andAge:(int)age{ if ( self = [super init] ) { _name = name; _age = age; } return self;}@endStudent.h繼承 person 類#import "Person.h"@interface Student : Person@property int no;//_no 是子類 student 自己的屬性- (id)initWithNo:(int)no;- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no;@endstudent.m#import "Student.h"@implementation Student- (id)initWithNo:(int)no{ if ( self = [super init] ) { _no = no; } return self;}// 要理解和接受這個做法和思想:// 父類的屬性交給父類方法去處理,子類方法處理子類自己的屬性- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no{// 將name、age傳遞到父類方法中進行初始化,no 在本類初始化使用了自訂的構造方法 if ( self = [super initWithName:name andAge:age]) { _no = no; } return self;}@endmain.m#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(int argc, const char * argv[]){ @autoreleasepool { Student *p = [[Student alloc] initWithName:@"Jim" andAge:29 andNo:10]; } return 0;}
每個對象建立之後,都有一個預設名,年齡,和學號,但是每個人的結果不一樣。此時,需要使用自訂構造方法初始化。否則重寫構造方法初始化的話(不帶參數)導致名稱都是一樣的。不符合程式要求。
首先,建立 student 對象,初始化對象指標 p(指向對象),先在本類找 initWithName 方法,確實有,然後進入執行,將name和age(都是繼承來的屬性)傳到父類方法中進行初始化:這樣分工明確(其實和 c++一樣的思路),自己做自己的事情,體現了良好的架構思想,還有這樣做的好處是,如果我們都統一在子類初始化,那麼一旦父類的成員變數名稱改變了,會導致子類的代碼出錯,需要修改,一旦工程較大,後果不堪設想,而分工明確,自己做自己的事情,把父類的成員讓父類去初始化,子類只管自己的,就能做到不變應萬變。