copy和mutableCopy

copy和mutableCopy
一、一個對象使用copy或mutableCopy方法能夠建立對象的副本
二、copy - 須要先實現NSCopying協議,建立的是不可變得副本(如NSString、NSArray、NSDictionary)
三、mutableCopy - 須要先實現NSMutableCopying協議,,建立的是可變副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
四、深複製:內容拷貝,源對象和副本指向不一樣的兩個對象。源對象引用計時器不變,副本計數器設置爲1
五、淺複製:指針拷貝,源對象和副本指向的是同一個對象。對象的引用計數器+1,其實至關於作了一次retain操做
六、只有不可變對象建立不可變副本(copy)纔是淺複製,其餘都是深複製
copy語法的目的:改變副本的時候,不會影響到源對象

爲自定義類添加複製功能
一、若是想自定義copy,那麼就必須遵照NSCopying,而且實現copyWithZone方法
二、若是想自定義mutableCopy,那麼就必須遵照NSMutableCopying,而且實現mutableCopyWithZone:方法
三、以copy爲例,建議用[self class]代替直接類名app

- (id)copyWithZone:(NSZone *)zone {
     id copy = [[[self  class ] allocWithZone:zone] init];
     // 作一些屬性的初始化
     return  copy;
}
 
// 演示字符串的拷貝
NSString *string = [[NSString alloc] initWithFormat:@ "age is %i" , 21]; // 1
// 產生了一個新的對象,引用計數器爲1.源對象的引用計數器不變
NSMutableString *str = [string mutableCopy]; // 1
NSLog(@ "str: &zi" , [str retainCount]);  // 1
NSLog(@ "string: %zi" , [string retainCount]); // 1
// str和string不是相同對象
NSLog(@ "%i" , str == string);
// 修改str看string是否有被修改
[str appendString@ "123456" ];
NSLog(@ "str = %@" , str);
NSLog(@ "string = %@" , string);
[str release]; // 0
[string release]; // 0
// 若是一個對象是不可變的,copy的對象也是不可變的,系統會直接返回被copy的對象自己
// copy產生的是不可變副本,因爲源對象自己就不可變,因此爲了性能着想,copy會直接返回源對象自己,至關於源對象的retain操做,引用計數器+1
NSString *s1  = [[NSString alloc] initWithFormat:@ "age is %i" , 21];
NSLog(@ "s1: %zi" , [s1 retainCount]); // 1
NSString *s2 = [s1 copy];
NSLog(@ "s1: %zi" , [s1 retainCount]); // 2
NSLog(@ "%i" , s2 == s1);


Student.h:函數

@interface Student : NSObject <NSCopying>
// copy表明setter方法會release舊對象,copy新對象
// 修改外面的變量,並不會影響到內部的成員變量
// 建議:NSString通常用copy策略,其餘對象通常用retain
@property (nonatomic, cop)NSString *name;
 
+ (id)studentWithName:(NSString *)name;
@end


Student.m:性能

@implementation Student
+ (id)studentWithName:(NSString *)name {
     Student *stu = [[[Student alloc] init] autorelease];
     stu.name = name;
     return  stu;
}
// 這裏建立的副本對象不要求釋放
- (id)copyWithZone:(NSZone *)zone {
     Student *copy = [[Student allocWithZone:zone] init];
     copy.name = self.name;
     return  copy;
}
 
- ( void )description {
     return  [NSString stringWithFormat:@ "[name = %@]" , _name];
}
 
- ( void )dealloc {
     [_name release];
     [super release];
}
@end


main.m:測試

// 這裏寫self class是爲了防止子類在建立的過程當中致使類型錯誤
Student *stu = [[[[self  class ] alloc] init] autorelease];
NSMutableString *string = [NSMutableString stringWithFormat@ "age is %i" , 21];
stu.name = string;
[string addendString:@ "abcd" ];
// 若是Student.h中的*name是retain,那麼修改了string,stu.name也被修改了
// 若是Student.h中的*name是copy,那麼修改了string,stu.name就不會被修改
NSLog(@ "name = %@" , stu.name);
NSLog(@ "string = %@" , string);
 
// Student的copy
Student *stu1 = [Student studentWithName:@ "student1" ];
// 若是Student沒有實現NSCopying協議,那麼會報錯:unrecognized selector sent to instance....
Student *stu2 = [stu1 copy];
stu2.name = @ "stu2" ;
NSLog(@ "stu1 = %@" , stu1);
NSLog(@ "stu2 = %@" , stu2);
[stu2 release];


類的本質
類自己也是一個對象,是一個Class類型的對象,簡稱類對象
Class類型的定義:
typedef struct objc_class *Class;
類名就表明着類對象,每一個類只有一個類對象
例如:
利用Class建立Person類對象
利用Person類對象建立Person類型的對象

獲取內存中的類對象:ui

Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
// 第一種方法
Class c1 = [p1  class ];
Class c2 = [p2  class ];
// 第二種方法
Class c3 = [Person  class ];
NSLog(@ "c1 = %p, c2 = %p, c3 = %p" , c1, c2, c3);


獲取的類對象(Class)能夠調用類方法,好比Person.h中有一個名爲test的類方法:atom

+ ( void )texst;


Person.m中有實現該方法:spa

+ ( void )test {
     NSLog(@ "調用了類方法test" );
}


測試:指針

Class c = [p1  class ];
[c test];


類的加載和初始化:
+load
一、在程序啓動的時候會加載全部的類和分類,並調用全部類和分類的+load方法
二、先加載父類,再加載子類:也就是先調用父類的+load方法,再調用子類的+load方法
三、先加載原始類,再加載分類
四、無論程序運行過程有沒有用到這個類,都會調用+load加載

+initialize
一、在第一次使用某個類時(好比建立對象等),就會調用一次+initialize方法
二、一個類只會調用一次+initialize方法,先調用父類的,再調用子類的

+ (void)load方法在程序啓動的時候,就會加載一次項目中全部的類和分類。類加載完畢後就會調用每一個類和分類的load方法。只會調用一次
load方法會從父類開始調用,再是子類,包括Category也會調用load方法
+ (void)initialize方法在第一次使用某個類的時候調用
initialize方法也是先初始化父類,再是子類

description方法
一、-description方法:使用NSLog和%@輸出某個對象時,會調用對象的-description方法,並拿到返回值進行輸出
二、+description方法:使用NSLog和%@輸出某個對象時,會調用類對象的+description方法,並拿到返回值進行輸

死循環陷阱:若是在-description方法中使用NSLog打印self

構造方法
做用:用來初始化對象的方法,是一個對象方法,"-"開頭
重寫構造方法的目的:爲了讓對象建立出來,成員變量就已經有一些固定的值
重寫構造方法的注意點:
一、先調用父類的構造方法([super init])
二、再進行子類的內部成員變量的初始化
例如:
重寫-init方法:code

- (id)init {
     // 1. 必定要調用回super的init方法:初始化父類中聲明的一些成員變量和其餘屬性
     self = [super init]; // 當前對象 self
     // 2. 若是對象初始化成功,纔有必要進行接下來的初始化操做
     if  (self != nil) {
         // 初始化成功
         _age = 10;
     }
     // 3. 返回一個已經初始化完畢的對象
     return  self;
}


父類的屬性交給父類方法去處理,子類方法處理子類本身的屬性
自定義構造方法的規範:
一、必定是對象方法,必定以"-"開頭
二、返回值通常是id類型
三、方法名通常以init開頭

NSLog輸出的一些補充(都是兩個下劃線"_"):orm

// 輸出當前函數名
NSLog(@ "%s\n" , __func__);
// 輸出行號
NSLog(@ "%d" , __LINE__);
// NSLog輸出C語言字符串的時候,不能有中文,可使用printf函數輸出
// NSLog(@"%s", __FILE__);
printf ( "%s\n" , __FILE__);
2014-12-08
iOS
244 瀏覽 1 回答

ARC
ARC的判斷準則:只要沒有強指針指向對象,就會釋放對象
ARC的特色:
一、不容許調用release、retain、retainCount
二、容許重寫dealloc,可是不容許調用[super deallo]
三、@property的參數:
    * strong:成員變量時強指針,至關於原來的retain(適用於OC對象類型)
    * weak:成員變量時弱指針,至關於原來的assign(適用於OC對象類型)
    * assign:適用於非OC對象類型

指針分兩種:
一、強指針:默認狀況下,全部的指針都是強指針 __strong
二、弱指針:__weak

Xcode是默認使用ARC的,若是某個.m文件真的不想使用ARC,能夠經過如下步驟來不適用ARC:
選擇Xcode右側項目樹的根,而後是TARGETS -> Build Phases -> Compile Sources,下拉,選擇目標.m文件,回車或者雙擊,彈出輸入框,輸入"-fno-objc-arc"回車,就能夠了,以下圖所示:

5486fd6b0001924105000173.jpg

 

若是開發環境是非ARC的,想要使用ARC的,將上面的"-fno-objc-arc"改爲"-f-objc-arc"就能夠了。

ARC循環引用
當兩端循環引用的時候,解決方案以下:
一、ARC
    1端用strong,另外一端用weak
二、非ARC
    1端用retain,另外一端用assign
例如:
在使用ARC下,有兩個類:Person、Dog,以下:
Person.h:

@ class  Dog;
 
@interface Person : NSObject
// 人有一隻狗,strong,強指針
@property (nonatomic, strong) Dog *dog;
@end


Person.m:

@implementation Person
- ( void )dealloc {
     NSLog(@ "Person---- dealloc" );
}
@end


Dog.h:

@ class  Dog;
 
@interface Dog: NSObject
// 狗有一個主人,strong,強指針
@property (nonatomic, strong) Person *person;
@end
 
Dog.m:
@implementation Dog
- ( void )dealloc {
     NSLog(@ "Dog ---- dealloc" );
}
@end


main.m:

Person *p = [[Person alloc] init];
Dog *d = [[Dog alloc] init];
// 第一種狀況:
// 當兩個互指以後,會出現內存泄露,兩個對象的dealloc沒有被調用,也就是兩個對象的內存沒有被釋放
p.dog = d;
d.person = p;

如圖所示:

5486fd7d000140a105000379.jpg

// 第二種狀況:
// 若是註釋上面兩行代碼中的任意一行,兩個對象均可以被釋放
// 由於當main函數執行完畢以後,對象p和d都被回收,可是兩個內存中的對象有強指針,不會被回收,因此會形成內存泄露
p.dog = d;
// d.person = p;

如圖所示:

5486fd860001834a02690435.jpg

 

若是將Dog中的person屬性改爲weak:

@property (nonatomic, weak) Person *person;

那麼,上面第二種狀況就變成了以下圖所示:

5486fd940001854a02620433.jpg

 

這樣的話,當程序運行結束,被回收的就是Person對象,既然Person對象被回收了,那麼Dog對象就沒有了強指針,也會被回收了。

相關文章
相關標籤/搜索