iOS開發(fā)幾年了,你清楚OC中的這些東西么!!!?

前言

幾年前筆者是使用Objective-C進行iOS開發(fā), 不過在兩年前Apple發(fā)布swift的時候,就開始了swift的學習, 在swift1.2發(fā)布后就正式并且一直都使用了swift進行iOS的開發(fā)了, 之后就是對swift持續(xù)不斷的學習, 近來swift3.0的發(fā)布, 更多的人會選擇swift來進行iOS的開發(fā)看上去更是成為了一種趨勢, 不過一個合格的iOS開發(fā)者對oc以及c語言的掌握是必不可少的技能, 本篇中主要是寫一些大家平時都可能用到但是不一定知道的oc的東西

  1. oc中的對象的創(chuàng)建: 首先會通過 +(id)alloc 動態(tài)的分配所有的變量以及父類定義的變量所需要的足夠內(nèi)存, 同時會清除所有的分配的內(nèi)存空間, 全部置為0
  • 同時接著需要調(diào)用class的 -(id)init 方法, 這個方法給每個變量設置初始值

  • 返回的類型為id, id是一個可以指向任意類型的指針(不用 * 號), 這個在一定程度上可以完成多態(tài)的效果

  • 對oc中的class文件的理解: class, extension, category
    ZJPerson.h文件


    Snip20160817_4.png

    ZJPerson.m文件


    Snip20160817_5.png

    ZJPerson.m文件
    m
  • [[XXObject alloc] init] 初始化方法不需要參數(shù)的時候, 和 [XXObject new] 方法相同

  • 通過字面量來初始化對象, 例如

    NSString *string = @"string"; == [[NSString alloc] initWithString:@"string"];等初始化方法
    NSNumber *myBOOL = @YES; == [[NSNumber alloc] initWithBool:YES];
    NSNumber *myFloat = @3.14f; ==
    NSNumber *myInt = @42; ==
    NSNumber *myLong = @42L; ==...
  • oc(c)中多行宏的定義(這個在swift...中更方便直接一個全局的函數(shù)就搞定了): 在除了最后一行的每一行結尾加一條反斜杠 \


    定義.png

    使用.png
  • 比較是否相同: 使用 if(a==b) {}, 如果a,b是對象類型, 那么比較的是指針是否相同, 而不是比較值是否相同, 如果a, b是基本類型(int, double...), 那么比較的是值是否相同; 使用if ([a isEqual: b]) { }, 則比較的是a,b的值是否相同
  • 初始化基本類型的時候盡量設置初始值, 因為編譯器分配的初始值并不確定, 但是對象類型會默認初始化為nil
  • 條件判斷: 當對象不為nil(有內(nèi)存地址)的時候, 或者基本類型非0, 或者bool類型為true, 這個時候條件都為真, 其他情況條件為假
  • oc中屬性的getter和setter
    @property (nonatomic) NSString *name;
  • 例如當有這樣一個name屬性的時候, 默認是readWrite的, 編譯器會自動生成一個set (setName:)和get(-(NSString *)name)方法, 這個時候可以通過set或者get方法訪問到name, 如果申明為(readonly), 那么將只會生成get方法
    [self setName:@"set name"];
    NSString *getName = [self name];
也可以通過點語法訪問(實際上是會自動調(diào)用set和get方法)
    self.name = @"set name";
    NSString *dotName = self.name;
  • 同時你可以重寫name的get(懶加載...)和setter(攔截set方法)...
    對應name屬性, 編譯器會生成(synthesize)一個 _name 允許我們直接通過指針訪問變量, 而不會調(diào)用get方法, 所以通過_xx訪問的變量不會調(diào)用懶加載(get方法), 所以在寫懶加載方法的時候, 不能使用self.xx(造成死循環(huán)), 而要使用_xx
    - (NSString *)name {
 // 這里面不能使用self.name , 因為點語法會調(diào)用這個get方法, 造成死循環(huán)
    if (_name == nil) {
        _name = @"name";
    }
    return _name;
}
  • 同時這個synthesize的名字我們是可以自己修改的, 使用如下的語法
    @synthesize name = customName;
  • 那么這個時候就不能通過 __name訪問到name了, 因為我們已經(jīng)指定了通過customName才能訪問到了
    NSString *getName = customName;
  • 當然如果, 你是這樣寫的 @synthesize name;, 并沒有指定名字, 這個時候訪問的時候就直接使用變量名而不需要加下劃線( _ )了 name = @"set name"; ??這個時候就比較爽了, 和swift,java這些一樣, 不需要self,this了;
  • oc的屬性默認是atomic(原子的), 也就是說是線程安全(只保證setter或者getter的這個過程是線程安全的, 不會被并不是我們經(jīng)常說的多線程操作一個屬性的時候的線程安全的概念)的, 這個時候是不允許重寫set和get方法的, 因為內(nèi)部的setter和getter會做出處理, 保證線程安全, 但是我們經(jīng)常使用的是noatomic, 因為訪問的速度比較快, 并且可以自己重寫getter和setter
  • oc中的對象是動態(tài)管理(內(nèi)存)的, 是分配在heap(堆)上所以需要一個指針來指向它(才能訪問), 所以對象類型需要用 星號 NSString * str;
  • oc中的對象管理在ARC下是用引用計數(shù)來管理的, 當有一個強引用對象A指向這個對象B的時候, B引用計數(shù)加一, 當這個對象A銷毀的時候,B的引用計數(shù)減一, 直到B的引用計數(shù)為0的時候就被自動銷毀, 當然這個時候如果A強引用B, B同時強引用A就造成了循環(huán)引用, 兩者都不會被銷毀, 就造成了內(nèi)存泄漏, 解決方法是將一方標記為 weak 或者unsafe_unretained(垂懸指針, 和swift中的[unowned self]類似, 所以運用不當會造成野指針的問題)
  • oc中的屬性默認是strong的, 所以需要顯示的指定為其他的(weak, unsafe_unretained...)
  • NSObject * __weak someObject = [[NSObject alloc] init];, 這個someObject沒有對象強引用他, 所以這行代碼之后會立馬被置為nil, NSObject * __weak someObject = self.someObject, 這個someObject在這行代碼之后不會立刻被置為nil, 而是會在所在的代碼塊結束后被置為nil
  • 對于屬性的賦值(深淺拷貝)
    @property (nonatomic) NSString *name;
    NSMutableString *str = [NSMutableString stringWithString:@"初始"];
    ViewController *ob = [ViewController new];
    ob.name = str;  ---- 淺拷貝
    NSLog(@"%@", ob.name); --- 初始
    [str appendString:@"+1"];
    NSLog(@"%@", ob.name); --- 初始+1
    這里出現(xiàn)ob.name改變的原因就是: 屬性name是strong(默認)類型的,ob.name = str; 這行代碼賦值后, 實際上只是name強指向了str, 所以當str的內(nèi)容改變的時候, ob.name也改變了
    NSLog(@"%@", ob.name); --- 初始
    str = [NSMutableString stringWithString:@"改變"];
    NSLog(@"%@", ob.name); --- 初始
    但是這樣的賦值, 直接改變str之后并不會影響原來的str的指針指向的內(nèi)容, 所以ob.name仍然指向原來的str, 因此內(nèi)容并未改變
    如果將上面的  ob.name = str; 改為 ob.name = [str mutableCopy]; 那么將上面的兩種操作都不會影響ob.name ---- 深拷貝
    如果name被修飾為copy
    @property (nonatomic, copy) NSString *name;
    那么上面的操作都不會改變ob.name的內(nèi)容 ---- 深拷貝
  • 分類(category)定義的函數(shù)和屬性在運行時中和原生的class中定義的東西并沒有區(qū)別At runtime, there’s no difference between a method added by a category and one that is implemented by the original class
  • 不過分類中定義的屬性, 編譯器并不會自動生成getter和setter, 以及_XX變量來訪問,需要自己提供getter和setter, 并且需要使用運行時才能綁定這個屬性到這個類中, 實現(xiàn)原生類中定義的屬性的效果
  ///例如可能是這樣的使用
    static const void *propertyKey = &propertyKey;
    /// 將value通過運行時綁定到self
    objc_setAssociatedObject(self, propertyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    /// 將value在運行時中通過propertyKey取出綁定的值
    id value = objc_getAssociatedObject(self, propertyKey);
  • 同時分類也可以用來將一個復雜的類中的代碼分塊(swift的extension可以有相似的作用), 使得代碼組織更好, 例如可以將tableView的delegate, 和Datasource在分類中實現(xiàn),
      @implementation ViewController(tableview)
      - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
...
@end
  • 但是在使用category來擴展Cocoa的原生類的時候, 要注意函數(shù)的命名如果是和原生已有的函數(shù)名相同,那么將會發(fā)生不可預料的結果(不能確定哪一個方法在運行時會被調(diào)用), 因此建議在自己的函數(shù)名前面加上前綴, 就像重寫 +load() 來實現(xiàn)各種黑魔法的時候也是可能會發(fā)生不可預料的結果, 因為同一個項目中可能有多個地方重寫了這個類的 +load方法
  • 初始化NSArray的時候, 如果通過NSArray *arr1 = @[object1, object2];, 不需要以nil結尾, 如果通過構造方法初始化, 則需要傳入nil結尾, 同時, 如果中間的對象有nil, 那么將在中間nil就結束了, NSArray *arr2 = [NSArray arrayWithObjects:object1, object2, object3, nil, object4, object5, nil] 這個arr2只可能會存儲第一個nil前的對象
  • 如果在數(shù)組中一定要存儲nil, 那么只能用NSNull來代替
  • 如果NSArray中存儲的是NSArray, NSDictionary, NSString, NSData, NSDate , NSNumber這些類型的對象, 那就可以直接寫入disk并且讀取disk的數(shù)據(jù)做持久化數(shù)據(jù)操作[array writeToURL:fileURL atomically:YES], 但是如果是有其他的類型, 就需要使用歸檔來實現(xiàn)了
  • 在for-in快速枚舉中, 不能夠修改(增刪)被枚舉的對象(數(shù)組, 字典,集合)
  • 在寫代碼的時候, 進行條件判斷的時候, 經(jīng)常會出現(xiàn)這樣的代碼 if (a = 1){...}, 這樣寫編譯器是會報錯的, 需要寫成 if(a == 1) {...}, 當然你非要使用一個等號也是可以的, 需要額外加一個括號, if ((a = 1)) {...}
  • 實際上絕大多數(shù)情況下都是我們寫條件判斷的時候都是使用==, 而非 =, 也就只有當我們寫構造方法的時候才可能會寫到 =,像這樣 if (self = [super init]) {...}, 其實這并不是使用=來判斷條件相等是正確的, 只是在這里, 通過[super init]方法會返回一個id對象, 通過, self = [super init], 把這個對象賦值給self, 這個時候的 if就是用來判斷, 被賦值后的self是否為nil, 而不是 self是否等于[super init]返回的對象.
  • 在oc中block是object類型的, 所以是可以存儲在NSArray...中, 同時在調(diào)用block的時候, 如果block為nil(未賦值), 那么程序?qū)rash.
  • oc中block可以捕獲變量, 什么意思呢 --- 就是block會默認捕獲到變量的值, 在之后不受到原來變量的改變的影響, 例如
     int anInteger = 42;
 
    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };
 
    anInteger = 84;
 
    testBlock(); ---- 輸出的值仍然為 42
  • 第二種block捕獲變量的方式, 是捕獲變量的指針, 被捕獲的變量值改變, 則block中的變量值也改變了,不過需要對變量進行__block標記, 例如上面的代碼, 只改變一點, 結果就變了
    __block int anInteger = 42;
 
    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };
 
    anInteger = 84;
 
    testBlock(); --- 輸出值這時是 84
  • 伴隨著block能夠捕獲變量的能力的一個問題就是, 循環(huán)引用, 在ARC中, 只要不是用到純C語言的庫,管理內(nèi)存的工作都不需要我們完成, 但是循環(huán)引用卻是我們需要解決的, 最常見的就是當block捕獲的變量是一個對象的屬性(方法)的時候, 也就是會捕獲到self, 那么這個時候就可能會造成循環(huán)引用(block屬性應該被標記為copy), 解決方法也很簡單, 使用一個對self弱引用的指針即可, 這個寫法就很多了, 筆者習慣的寫法是: __weak typeof(self) weakSelf = self;, 那么在block中使用weakSelf替代self調(diào)用相關的屬性或者方法, 就不會造成循環(huán)引用
  • 使用weakSelf能夠解決block捕獲self造成的循環(huán)引用的內(nèi)存泄漏問題, 但是帶來的另一個問題就是, 特別是在多線程中,可能在block中代碼正在執(zhí)行的時候, self被銷毀了, 因為使用weakSelf捕獲到的是self的弱引用, 那么后續(xù)的代碼就不能夠繼續(xù)執(zhí)行了, 這個時候為了保證在這個block中self即使被銷毀block里面的代碼也能正常執(zhí)行, 我們需要的另一個操作就是, 將weakSelf強引用一次, 讓他的引用計數(shù)加1, 就能處理這個問題, 就是Apple在wwdc中提到的weak-strong-dance, 筆者習慣的書寫方式是: __strong typeof(self) strongSelf = weakSelf;,,, 當然這個必須要明白的是, 這個block里面的strongSelf能夠保證里面代碼執(zhí)行完畢的前提是程序能夠執(zhí)行到block, 如果在執(zhí)行block之前self已經(jīng)被銷毀了, 那么這個block肯定是不會被調(diào)用的(block的引用計數(shù)已經(jīng)為0).

按照慣例, 我的文章里面都會有demo的, 但是這篇文章沒有demo, 因為這只是寫一點點語法的東西

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末可帽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壁查,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馋记,死亡現(xiàn)場離奇詭異驰后,居然都是意外死亡肆资,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門灶芝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郑原,“玉大人,你說我怎么就攤上這事夜涕》咐纾” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵女器,是天一觀的道長酸役。 經(jīng)常有香客問我,道長驾胆,這世上最難降的妖魔是什么涣澡? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮丧诺,結果婚禮上入桂,老公的妹妹穿的比我還像新娘。我一直安慰自己驳阎,他們只是感情好事格,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搞隐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪远搪。 梳的紋絲不亂的頭發(fā)上劣纲,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音谁鳍,去河邊找鬼癞季。 笑死劫瞳,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的绷柒。 我是一名探鬼主播志于,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼废睦!你這毒婦竟也來了伺绽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤嗜湃,失蹤者是張志新(化名)和其女友劉穎奈应,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體购披,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡杖挣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了刚陡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惩妇。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筐乳,靈堂內(nèi)的尸體忽然破棺而出歌殃,到底是詐尸還是另有隱情,我是刑警寧澤哥童,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布挺份,位于F島的核電站,受9級特大地震影響贮懈,放射性物質(zhì)發(fā)生泄漏匀泊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一朵你、第九天 我趴在偏房一處隱蔽的房頂上張望各聘。 院中可真熱鬧,春花似錦抡医、人聲如沸躲因。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽大脉。三九已至,卻和暖如春水孩,著一層夾襖步出監(jiān)牢的瞬間镰矿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工俘种, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秤标,地道東北人绝淡。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像苍姜,于是被迫代替她去往敵國和親牢酵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,125評論 29 470
  • 1衙猪、截取字符串”20 | http://www.baidu.com”中馍乙,”|”字符前面和后面的數(shù)據(jù),分別輸出它們 ...
    強子ly閱讀 2,917評論 8 46
  • Object-c的類可以多重繼承么?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好...
    small_Sun閱讀 734評論 0 0
  • 親愛的佳佳屈嗤,你好潘拨! 又是10天過去了,不知道你是否對生命有了新的感悟饶号?每次當你瞪著你的大眼睛铁追,愣愣地瞅著周圍的時候...
    年糕佳爸閱讀 300評論 0 0
  • 君子之德,最高境界是善與人同茫船,不追求“自己的東西”琅束,善為天下公,無我無他算谈,舍己從人涩禀,與人為善。與人為善然眼,不是對人好...
    華杉2009閱讀 1,080評論 0 3