內(nèi)存中的五大區(qū)域
棧:存儲局部變量
堆:程序員手動申請的字節(jié)空間
BSS段:存儲未被初始化的全局變量 靜態(tài)變量
數(shù)據(jù)段(常量區(qū)) :存儲已經(jīng)被初始化的全局變量,靜態(tài)變量,常量數(shù)據(jù),直到程序結束的時候才會被回收
代碼段:存儲程序的代碼,如類第一次訪問時,就把類加載到代碼段
類加載
1).在創(chuàng)建對象的時候.肯定是需要訪問類的
2).聲明1個類的指針變量也會訪問類的
在程序運行期間,當某個類第一次被訪問到的時候,會將這個類存儲到內(nèi)存中的代碼段區(qū)域,這個過程叫做類加載
只有類在第一次被訪問的時候,才會做類加載
一旦類被加載到代碼段以后,直到程序結束的時候才會被釋放
- 類在代碼段中存儲的步驟?
a.現(xiàn)在代碼斷種創(chuàng)建個Class對象,Class是Foundation框架中的一個類.
b.將類的信息存儲在Class對象中,這個Class至少有3個屬性
類名,屬性,方法,注意:還有一個isa指針,指向父類的類對象
存儲類的這個Class對象,我們也叫類對象
任何存儲在內(nèi)存中的數(shù)據(jù)都有一個數(shù)據(jù)類型,任何在內(nèi)存中申請的空間也有自己的類型
instancetype
當返回的是該類的對象時,用instancetype
static
1).修飾局部變量(在方法里面的變量).
使局部變量只初始化一次,局部變量在程序中只有一份內(nèi)存,使和這個變量擁有記憶功能(可以被賦值).局部變量的作用域不變,但是生命周期變了(直到程序結束才銷毀)
2).修飾的全局變量
擁有局部變量修飾時候的屬性,不同的是該靜態(tài)變量只能被本文件訪問(作用域在本文件)
訪問修飾符
用來修飾屬性,可以限定對象的屬性在哪個范圍之中訪問
@private:私有 被@private修飾的屬性只能在本類的內(nèi)部訪問(本類的實現(xiàn)中訪問)
@protected:受保護的,被@protected修飾的屬性只能在本類和本類的子類中訪問
@package:被@package修飾的屬性,可以在當前的框架在訪問
@public:公共的,被@public修飾的屬性,可以在任意的地方訪問
- 注意:如果不為屬性指定訪問修飾符,那么默認的就是@protected
- 子類仍然可以繼承父類的私有屬性,只不過,在子類中無法去直接訪問從父類繼承過來的私有屬性,如果父類中有一個方法在為屬性賦值或者取值,那么子類可以調(diào)用這個方法間接的訪問父類的私有屬性
- 訪問修飾符的作用域:從寫訪問修飾符的地方開始往下,直到遇到另外一個修飾符或者結束大括號為止,中間的所有屬性都應用這個訪問修飾符
里氏替換原則
父類指針指向子類對象
當一個父類指針指向子類對象的時候,通過這個父類指針就只能去調(diào)用子類中的父類成員,子類獨有的成員無法訪問
當一個父類的指針指向子類對象時,通過這個父類指針調(diào)用的方法,如果在子類對象中重寫了,調(diào)用的就是子類重寫的方法
多態(tài)
指的是同一個行為,對于不同的事物具有不同的表現(xiàn)形式
結構體和類的區(qū)別
1).結構體只能封裝數(shù)據(jù),而類不僅可以封裝數(shù)據(jù),還可以封裝方法
2).結構體變量分配在棧空間(如果是局部變量的情況下),而對象分配在堆空間
棧空間:空間相對較小,但是存儲在棧中的數(shù)據(jù)訪問效率更高一些
堆空間:空間相對較大,但是存儲在棧中的數(shù)據(jù)訪問效率相對要低
3).賦值
//結構體 Student
Student s = {"小明",19}
//類 Student
Student *s = [Student new];
s.name= @"小明";
s.age = 19;
id
萬能指針.也可以作為返回值.在編譯階段不會去檢查
instancetype
只能作為返回值,返回當前對象
dealloc
重寫父類的dealloc方法時,必須要調(diào)用父類的dealloc方法,即[super dealloc].且要放到方法的最后面.在ARC的機制下,retain,release和dealloc這些方法無法調(diào)用,即[super dealloc]不要去調(diào)用
對象回收的本質
所謂的對象回收,指的是對象占用的空間可以分配給別人,當這個對象占用的空間還沒分配給別人使用之前,其實這個對象的數(shù)據(jù)還在
野指針
已經(jīng)釋放的對象指針,就稱為野指針
僵尸對象
一個已經(jīng)被釋放的對象,但這個對象的空間還沒分配給別人,這樣的對象叫做僵尸對象.
我們通過野指針去訪問僵尸對象的時候,有可能沒問題,也有可能有問題.當僵尸對象占用的空間還沒有分配給別人之前,這是可以訪問的,當僵尸對象占用的空間已經(jīng)分配給別人了,就不允許訪問
@property參數(shù)
@property(參數(shù)1,參數(shù)2,參數(shù)3...)數(shù)據(jù)類型 變量名稱
1.與多線程有關的兩個參數(shù):atomic,nonatomic
atomic:默認值,如果寫atomic,這個時候生成的setter方法的代碼就會被加上一把線程安全鎖.特點:安全,效率低
nonatomic:nonatomic,這個時候生成的setter方法的代碼就不會被加上一把線程安全鎖.特點:不安全,效率高
建議:使用nonatomic
2.與生成的setter方法實現(xiàn)有關的參數(shù):assign,retain(MRC),strong,weak,copy....
assign:默認值,生成的setter方法實現(xiàn)就是直接賦值.在ARC和MRC下都可以使用
retain:只能用在MRC模式下,生成的setter實現(xiàn)就是標準的mrc內(nèi)存格式下的實現(xiàn).先判斷舊的對象和新的對象是不是同一個值,如果不是,就realease舊的,retain新的
使用:當屬性的類型是oc對象的時候就是用retain.當屬性的類型是非oc對象就是用assign
在ARC機制下,當屬性類型是oc對象時,不能使用retain,絕大數(shù)時間應該使用strong
strong強類型指針,weak弱類型指針
3.與生成只讀,讀寫有關的參數(shù):readonly,readwrite
readwrite:默認值,代表同時生成getter和setter.
readonly:只會生成getter,不會生成setter
4.與生成getter,setter方法名字有關的參數(shù):getter,setter
默認情況下,@property生成的getter,setter都是標準的名字.其實我們也可以通過參數(shù)來著指定@property生成的方法的名字
@property(nonatomic,assign,getter=自定義名字)int age
@class和#improt的區(qū)別
import是將指定的文件內(nèi)容拷貝到寫指令的地方,@class并不會拷貝任何內(nèi)容.只是告訴編譯器,這是一個類,這樣編譯器在編譯的時候才可以知道這是一個類
Block
block在oc中是一種數(shù)據(jù)類型
,所以可以作為變量,參數(shù),返回值使用.
語法:返回值類型 (^block名字)(參數(shù)列表) = ^返回值類型(參數(shù)列表){...}
int (^MyBlock)(Nsstring *name) = ^void(Nsstring *name){
NSLog(@"%@",name);
}
//關于block的簡寫
//如果block表達式(代碼段)中沒有返回值,那么代碼段中的void可以省略
void (^MyBlock)(Nsstring *name) = ^(Nsstring *name){
...
}
//注意:1.代碼段中的void可以省略,聲明block變量的返回值無論是什么都不可以省略的.2.如果代碼段中沒有參數(shù),代碼段中表示參數(shù)列表的()也可以省略掉,聲明block變量的參數(shù)()不能省略
void (^MyBlock2)() = ^{
...
};
//聲明block變量的時候,如果有指定參數(shù),可以只寫參數(shù)的類型而不寫參數(shù)的名稱
int (^MyBlock3)(int,int) = ^(int num1,int num2){
......
};
//注意,這個地方是聲明block變量的時候,寫代碼段的時候類型和形參名字都要寫
//在寫代碼段中,其實無論時候有返回值,都可以省略返回值類型,那么代碼段的返回值類型就感覺代碼段中代碼決定,如果代碼中的返回值和聲明block的返回值不一樣,那么程序就會報錯
NSString (^Block4)(NSString) = (NSString *str){
return @"你高啊"
};
//用typedef聲明block
typedef 返回值類型(^Block類型新名)(參數(shù)列表)
//關于block訪問外部變量
1).在block代碼塊內(nèi)部可以取定義在外部的局部變量和全局變量的值.
2).在block代碼塊內(nèi)部可以修改全局變量的值,但不能修改定義在外部的局部變量的值,如果非要改,在局部變量前面加__block的修飾符.
當block作為函數(shù)的返回值時,就必須要使用typedef來定義block的類型,不然會報錯
typedef
使用場景:將一個長類型定義成一個短類型.
在使用block時,可以將長的block聲明定義成一個變量
//如系統(tǒng)的NSUInteger定義
typedef unsigned long NSUInteger;
---------
NS_ASSUME_NONNULL_BEGIN
typedef void (^Block)(NSString *str);
@interface Person : NSObject
@property(nonatomic, copy)Block block;
@end
NS_ASSUME_NONNULL_END
協(xié)議:protocol
作用:專門用來聲明一大堆方法.(不能聲明屬性,也不能實現(xiàn)方法,只能用來寫方的聲明).只要某個類遵守了這個協(xié)議,就相當于擁有個這個協(xié)議中的所有方法聲明,而不用自己去定義
///協(xié)議的聲明
@protocol 協(xié)議名稱 <NSObject>
方法的聲明
@end
///某個類遵守了協(xié)議
@interface 類名: 父類名<協(xié)議名稱>
@end
@required和@optional是專門修飾協(xié)議中的方法.默認是@required
在協(xié)議中被@required修飾的方法,那么遵守這個協(xié)議的類必須要實現(xiàn)這個方法,否則編譯器會發(fā)出警告.
在協(xié)議中,被@optional修飾的方法,那么遵守協(xié)議的這個類可以實現(xiàn)這個方法,也可以不實現(xiàn)這個方法,編譯器不會發(fā)出警告
協(xié)議與協(xié)議之間可以相互繼承.語法:
@protocol 協(xié)議名稱 <父協(xié)議名稱>
@end
通過協(xié)議的繼承,子協(xié)議中不僅有自己的方法的聲明,還有父協(xié)議中的所有方法的聲明.如果一個類遵守了某個協(xié)議,那么這個類就擁有了這個協(xié)議和這個協(xié)議的父協(xié)議的所有方法的聲明
字符串的 == 判斷(不要用==去判斷字符串是否相等)
NSString *str1 = @"你好";
NSString *str2 = [NSString stringwithFormat:@"你好"];
Bool res = str1 == str2;
NSLog(@"%@",res); //輸出結果是NO
NSString *str3 = @"你好";
NSString *str4 = @"你好";
Bool re1 = str3 == str4;
NSLog(@"%@",res1); //輸出結果是YES
//注意:字符串==判斷是判斷兩個字符串指針的值.用NSString *str1 = @"你好";定義的字符串在常量區(qū)存儲,而且相同的字符串不會重復創(chuàng)建,[NSString stringwithFormat:@"你好"];創(chuàng)建的字符串在堆區(qū),相同的字符串也不會重復創(chuàng)建
copy 拷貝
1).copy是一個方法,定義在NSObject類之中,作用:拷貝對象
NSString -->copy--->不可變字符串 沒有產(chǎn)生新對象,而是直接返回原對象的地址,這種拷貝也稱淺拷貝
NSMutableString-->copy--->是一個不可變字符串對象,產(chǎn)生一個新的對象,這樣的拷貝成為深拷貝
2).mutableCopy定義在NSObject類之中,作用:拷貝對
NSString--->mutableCopy---->可變字符串對象,深拷貝
NSMutableString-->mutableCopy--->可變字符串對象,產(chǎn)生一個新的對象為深拷貝
- 總結:
- copy出來的字符串一定是不可變字符串,如果傳入的是可變字符串,會發(fā)生深拷貝,否則是淺拷貝
- mutableCopy一定是深拷貝,拷貝出來的一定是可變字符串或者數(shù)組,即使傳入的是不可變字符串或者數(shù)組
為什么NSString使用copy修飾也就可以理解了帆吻。使用copy修飾之后士聪,即使屬性拷貝來自可變字符串身腻,也會被深拷貝成不可變字符串能庆,也就是源字符串修改之后不會影響到屬性字符串讶请,增強了代碼的健壯性
%p 打印的是指針變量的值.%@ 打印的是指針指向的對象