OC基礎易忘知識點

屏幕快照 2017-06-22 上午12.15.06.png

];
p->_size = 3.5;
p->_color = 0;
p->_model = 4;
p->_cpu = 1;


####類

創建對象的時候返回的地址其實就是對象的第0個屬性的地址
     但是需要注意的是: 對象的第0個屬性并不是我們編寫的屬性, 而是一個叫做isa的屬性
     isa是一個指針, 占8個字節
     
     其實類也是一個對象, 也就意味著Person也是一個對象
     平時我們所說的創建對象其實就是通過一個 類對象 來創建一個 新的對象
     類對象是系統自動幫我們創建的, 里面保存了當前對象的所有方法
     而實例對象是程序自己手動通過new來創建的, 而實例對象中有一個isa指針就指向了創建它的那個類對象

如下圖:


![類對象](http://upload-images.jianshu.io/upload_images/2264508-a9485608a39cffd8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#####成員變量,局部變量,全局變量

    寫在類聲明的大括號中的變量, 我們稱之為 成員變量(屬性, 實例變量)
    成員變量只能通過對象來訪問
    注意: 成員變量不能離開類, 離開類之后就不是成員變量 
    成員變量不能在定義的同時進行初始化
    存儲: 堆(當前對象對應的堆的存儲空間中)
    存儲在堆中的數據, 不會被自動釋放, 只能程序員手動釋放

    寫在函數和大括號外部的變量, 我們稱之為全局變量
    作用域: 從定義的那一行開始, 一直到文件末尾
    局部變量可以先定義在初始化, 也可以定義的同時初始化
    存儲: 靜態區
    程序一啟動就會分配存儲空間, 直到程序結束才會釋放

    寫在函數或者代碼塊中的變量, 我們稱之為局部變量
    作用域: 從定義的那一行開始, 一直到遇到大括號或者return
    局部變量可以先定義再初始化, 也可以定義的同時初始化
    存儲 : 棧
    存儲在棧中的數據有一個特點, 系統會自動給我們釋放

#####點語法

    如果給屬性提供了getter和setter方法, 那么訪問屬性就又多了一種訪問方式 , 點語法
    點語法其實它的本質是調用了我們的setter和getter方法
    點語法是一個編譯器的特性, 會在程序翻譯成二進制的時候將.語法自動轉換為setter和getter方法
    如果點語法在=號的左邊, 那么編譯器會自動轉換為setter方法
    如果點語法在=號的右邊, 或者沒有等號, 那么編譯器就會自動轉換為getter方法
     點語法的注意點:
     點語法一般用于給成員變量賦值, 如果不是給成員 
     變量賦值一般情況下不建議使用, 但是也可以使用     
     如調用test方法,可以寫成p.test,相當于[p test]


#####多態

     多態是事物的多種表現形態

     在程序中如何表現:
     父類指針指向子類對象
     
     優點:
     提高了代碼的擴展性
     
     注意點:
     如果父類指針指向子類對象, 如果需要調用子類特有的方法, 必須先強制類型轉換為子類才能調用
    
    動態類型: 在編譯的時候編譯器只會檢查當前類型   對應的類中有沒有需要調用的方法
    在運行時,系統會自動判斷a1的真實類型

例如:

Animal:動物類

import <Foundation/Foundation.h>

@interface Animal : NSObject
{
int _age;
}

  • (void)eat;
    @end

import "Animal.h"

@implementation Animal

  • (void)eat
    {
    NSLog(@"吃東西");
    }
    @end

Dog狗類,繼承動物類:

import <Foundation/Foundation.h>

import "Animal.h"

@interface Dog : Animal

  • (void)kanJia;
    @end

import "Dog.h"

@implementation Dog

  • (void)eat
    {
    NSLog(@"啃骨頭");
    }

  • (void)kanJia
    {
    NSLog(@"看家, 旺旺叫");
    }
    @end

Animal *a1 = [Dog new];
[a1 eat];

編譯時因為動物有eat方法,不報錯。但是運行時判斷為dog對象,調用dog對象的eat方法


#####實例變量修飾符

@public 就是實例變量修飾符
 
 @public
 >可以在其它類中訪問被public修飾的成員變量
 >也可以在本類中訪問被public修飾的成員變量
 >可以在子類中訪問父類中被public修飾的成員變量
 
 @private
 >不可以在其它類中訪問被private修飾的成員變量
 >可以在本類中訪問被private修飾的成員變量
 >不可以在子類中訪問父類中被private修飾的成員變量
 
 @protected
 >不可以在其它類中訪問被protected修飾的成員變量
 >可以在本類中訪問被protected修飾的成員變量
 >可以在子類中訪問父類中被protected修飾的成員變量
 
 注意: 默認情況下所有的實例變量都是protected
 
 @package
 >介于public和private之間的
 如果是在其它包中訪問那么就是private的
 如果是在當前代碼所在的包種訪問就是public的

#####description
    NSlog(@"",moumoumou)
    %@是用來打印對象的, 其實%@的本質是用于打印字符串
    只要利用%@打印某個對象, 系統內部默認就會調用父類的description方法
    調用該方法, 該方法會返回一個字符串, 字符串的默認格式 <類的名稱: 對象的地址>
    可以重寫description方法, 返回我們需要打印的內容
    只要利用%@打印對象, 就會調用description
    如果通過%@打印對象就會調用-號開頭的
    如果通過%@打印類對象就會調用+號開頭的

    建議: 在description方法中盡量不要使用self來獲取成員變量
    因為如果你經常在description方法中使用self, 可能已不小心就寫成了 %@, self
    如果在description方法中利用%@輸出self會造成死循環,例如下面:

  • (NSString *)description
    {
    return [NSString stringWithFormat:@"%@", self];
    }

####@porperty

在Xocde4.4之前, 可以使用@porperty來代替getter/setter方法的聲明
 也就是說我們只需要寫上@porperty就不用寫getter/setter方法的聲明

 @synthesize是一個編譯器指令, 它可以簡化我們getter/setter方法的實現
 
 什么是實現:
 在聲明后面寫上大括號就代表著實現
  (1)在@synthesize后面告訴編譯器, 需要實現哪個@property生成的聲明
  (2)告訴@synthesize, 需要將傳入的值賦值給誰和返回誰的值給調用者
    如果在@synthesize后面沒有告訴系統將傳入的值賦值給誰, 系統默認會賦值給和@synthesize后面寫得名稱相同的成員變量

例如:
@synthesize age;就代表實現age成員變量的get, set,不是_age.

從Xcode4.4以后apple對@property進行了一個增強, 以后只要利用一個@property就可以同時生成setter/getter方法的聲明和實現
 沒有告訴@property要將傳入的參數賦值給誰, 默認@property會將傳入的屬性賦值給_開頭的成員變量
 
 @property有一個弊端: 它只會生成最簡單的getter/setter方法的聲明和實現, 并不會對傳入的數據進行過濾
 如果想對傳入的數據進行過濾, 那么我們就必須重寫getter/setter方法
 如果不想對傳入的數據進行過濾, 僅僅是提供一個方法給外界操作成員變量, 那么就可以使用@property
 
 如果利用@property來生成getter/setter方法, 那么我們可以不寫成員變量, 系統會自動給我們生成一個_開頭的成員變量
 注意: @property自動幫我們生成的成員變量是一個私有的成員變量, 也就是說是在.m文件中生成的, 而不是在.h文件中生成的

如果重寫了setter方法, 那么property就只會生成getter方法
如果重寫了getter方法, 那么property就只會生成setter方法
如果同時重寫了getter/setter方法, 那么property就不會自動幫我們生成私有的成員變量

####id類型

id是一個數據類型, 并且是一個動態數據類型

默認情況下所有的數據類型都是靜態數據類型
     靜態數據類型的特點: 
     在編譯時就知道變量的類型, 
     知道變量中有哪些屬性和方法
     在編譯的時候就可以訪問這些屬性和方法, 
     并且如果是通過靜態數據類型定義變量, 如果訪問了不屬于靜態數據類型的屬性和方法, 那么編譯器就會報錯
     
動態數據類型的特點:
     在編譯的時候編譯器并不知道變量的真實類型, 只有在運行的時候才知道它的真實類型
     并且如果通過動態數據類型定義變量, 如果訪問了不屬于動態數據類型的屬性和方法, 編譯器不會報錯

#####類的本質

類其實也是一個對象, 這個對象會在這個類第一次被使用的時候創建
     只要有了類對象, 將來就可以通過類對象來創建實例對象
     實例對象中有一個isa指針, 指向創建自己的類對象
     
 類對象中保存了當前對象所有的對象方法
     當給一個實例對象發送消息的時候, 會根據實例對象中的isa指針去對應的類對象中查找


![Snip20150623_6.png](http://upload-images.jianshu.io/upload_images/2264508-1a692817a227e781.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


啟動過程:

只要程序啟動就會將所有類的代碼加載到內存中, 放到代碼區。load方法會在當前類被加載到內存的時候調用, 有且僅會調用一次。如果存在繼承關系, 會先調用父類的load方法, 再調用子類的load方法。當當前類第一次被使用的時候就會調用(創建類對象的時候)。initialize方法在整個程序的運行過程中只會被調用一次, 無論你使用多少次這個類都只會調用一次。initialize用于對某一個類進行一次性的初始化。initialize和load一樣, 如果存在繼承關系, 會先調用父類的initialize再調用子類的initialize。


#####內存管理

野指針:
只要一個對象被釋放了, 我們就稱這個對象為 "僵尸對象"。當一個指針指向一個僵尸對象, 我們就稱這個指針為野指針。只要給一個野指針發送消息就會報錯。為了避免給野指針發送消息會報錯, 一般情況下, 當一個對象被釋放后我們會將這個對象的指針設置為空指針。

#####@class

應用場景1:

聲明一個類

應用場景2:

防止循環拷貝,造成死循環。(import引入會拷貝文件內容,如果.h1引入.h2,.h2又引入.h1,會造成循環拷貝)

#####Block

MRC:

返回值 (^block名字 )(參數列表)

取別名

typedef (^bieblock)(int,int)

就可以用 bieblock來定義新block,類型為上面

如:

bieblock block1;
block1 = ^(int value1, int value2){
        return value1 + value2;
    };

// 1.block中可以訪問外面的變量
/*
int a = 10;
void (^myBlock)() = ^{
NSLog(@"a = %i", a);
};
myBlock();

打印a = 10

// 2.block中可以定義和外界同名的變量, 并且如果在block中定義了和外界同名的變量, 在block中訪問的是block中的變量
/*
int a = 10;
void (^myBlock)() = ^{
int a = 20;
NSLog(@"a = %i", a);
};
myBlock();
*/

打印a = 20;

//

// 3.默認情況下, 不可以在block中修改外界變量的值
// 因為block中的變量和外界的變量并不是同一個變量
// 如果block中訪問到了外界的變量, block會將外界的變量拷貝一份到堆內存中
// 因為block中使用的外界變量是copy的, 所以在調用之前修改外界變量的值, 不會影響到block中copy的值
/*
int a = 10;
NSLog(@"&a = %p", &a);
void (^myBlock)() = ^{
// a = 50;
NSLog(@"&a = %p", &a);
NSLog(@"a = %i", a);
};
a = 20;
myBlock();
*/

/*
// 如果想在block中修改外界變量的值, 必須在外界變量前面加上__block
// 如果在block中修改了外界變量的值, 會影響到外界變量的值
__block int a = 10;
NSLog(@"&a = %p", &a);
void (^myBlock)() = ^{
a = 50;
NSLog(@"&a = %p", &a);
NSLog(@"a = %i", a);
};
myBlock();
NSLog(@"a = %i", a);
*/

/*

// 默認情況下block存儲在棧中, 如果對block進行一個copy操作, block會轉移到堆中
// 如果block在棧中, block中訪問了外界的對象, 那么不會對對象進行retain操作
// 但是如果block在堆中, block中訪問了外界的對象, 那么會對外界的對象進行一次retain

// 如果在block中訪問了外界的對象, 一定要給對象加上__block, 只要加上了__block, 哪怕block在堆中, 也不會對外界的對象進行retain
// 如果是在ARC開發中就需要在前面加上__weak

內存管理:

MRC:管理block
總結:只要block沒有引用外部局部變量,block放在全局區
只要Block引用外部局部變量,block放在棧里面.
block只能使用copy,不能使用retain,使用retain,block還是在棧里面
只要block沒有引用外部局部變量,block放在全局區

ARC:管理block
只要block引用外部局部變量,block放在堆里面

    block使用strong.最好不要使用copy

#####copy屬性

// 如果是通過不可變對象調用了copy方法, 那么不會生成一個新的對象
// 原因: 因為原來的對象是不能修改的, 拷貝出來的對象也是不能修改的
// 既然兩個都不能修改, 所以永遠不能影響到另外一個對象, 那么已經符合需求
// 所以: OC為了對內存進行優化, 就不會生成一個新的對象(比如nsstring->nsstring)

 正是因為調用copy方法有時候會生成一個新的對象, 有時候不會生成一個新的對象
     所以: 如果沒有生成新的對象, 我們稱之為淺拷貝, 本質就是指針拷貝
          如果生成了新的對象, 我們稱之為深拷貝, 本質就是會創建一個新的對象

// 3.注意點: copy block之后引發循環引用(MRC中)
(
    // 如果對象中的block又用到了對象自己, 那么為了避免內存泄露, 應該將對象修飾為__block
    __block Person *p = [[Person alloc] init]; // 1
    p.name = @"lnj";
    NSLog(@"retainCount = %lu", [p retainCount]);
    p.pBlock = ^{
        NSLog(@"name = %@", p.name); // 2
    };
    NSLog(@"retainCount = %lu", [p retainCount]);
    p.pBlock();
    
   [p release]; // 1
)


![屏幕快照 2017-06-22 上午12.10.13.png](http://upload-images.jianshu.io/upload_images/2264508-a8b0c54130bc743c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



![Uploading 屏幕快照 2017-06-22 上午12.15.06_880264.png . . .]
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容