1.weak 與 assign 不同
什麼情況使用 weak 關鍵字?
-
在ARC中,在有可能出現循環引用的時候,往往要通過讓其中一端使用weak來解決,比如:delegate代理屬性
-
自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用weak,自定義IBOutlet控件屬性一般也使用weak;當然,也可以使用strong。在下文也有論述:《IBOutlet連出來的視圖屬性爲什麼可以被設置成weak?》
不同點:
-
weak
此特質表明該屬性定義了一種“非擁有關係” (nonowning relationship)。爲這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值。此特質同assign類似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。 而assign
的“設置方法”只會執行鍼對“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作。 -
assigin 可以用非OC對象,而weak必須用於OC對象
ps:weak屬性不需要在dealloc中置nil
2.copy 關鍵字
用途:
- NSString、NSArray、NSDictionary 等等經常使用copy關鍵字,是因爲他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary;他們之間可能進行賦值操作,爲確保對象中的字符串值不會無意間變動,應該在設置新屬性值時拷貝一份。
如:@property (copy) NSMutableArray *array;兩個問題:1、添加,刪除,修改數組內的元素的時候,程序會因爲找不到對應的方法而崩潰.因爲copy就是複製一個不可變NSArray的對象;2、使用了atomic屬性會嚴重影響性能 ;
block 使用 copy 是從 MRC 遺留下來的“傳統”,在 MRC 中,方法內部的 block 是在棧區的,使用 copy 可以把它放到堆區.在ARC中寫不寫都行:對於 block 使用 copy 還是 strong 效果是一樣的,但寫上 copy 也無傷大雅,還能時刻提醒我們:編譯器自動對 block 進行了 copy 操作。如果不寫 copy ,該類的調用者有可能會忘記或者根本不知道“編譯器會自動對 block 進行了 copy 操作”,他們有可能會在調用之前自行拷貝屬性值。這種操作多餘而低效。
用@property聲明的NSString(或NSArray,NSDictionary)經常使用copy關鍵字,爲什麼?如果改用strong關鍵字,可能造成什麼問題?
- 因爲父類指針可以指向子類對象,使用copy的目的是爲了讓本對象的屬性不受外界影響,使用copy無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
- 如果我們使用是strong,那麼這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那麼會影響該屬性.
copy此特質所表達的所屬關係與strong類似。然而設置方法並不保留新值,而是將其“拷貝” (copy)。 當屬性類型爲NSString時,經常用此特質來保護其封裝性,因爲傳遞給設置方法的新值有可能指向一個NSMutableString類的實例。這個類是NSString的子類,表示一種可修改其值的字符串,此時若是不拷貝字符串,那麼設置完屬性之後,字符串的值就可能會在對象不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變” (immutable)的字符串,確保對象中的字符串值不會無意間變動。只要實現屬性所用的對象是“可變的” (mutable),就應該在設置新屬性值時拷貝一份,以防止之後“值”被可變。
爲了理解這種做法,首先要知道,對非集合類對象的copy操作:
在非集合類對象中:對immutable對象進行copy操作,是指針複製,mutableCopy操作時內容複製;對mutable對象進行copy和mutableCopy都是內容複製。用代碼簡單表示如下:
- [immutableObject copy] // 淺複製
- [immutableObject mutableCopy] //深複製
- [mutableObject copy] //深複製
- [mutableObject mutableCopy] //深複製
比如以下代碼:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
查看內存,會發現 string、stringCopy 內存地址都不一樣,說明此時都是做內容拷貝、深拷貝。即使你進行如下操作:
[string appendString:@"origion!"]
stringCopy的值也不會因此改變,但是如果不使用copy,stringCopy的值就會被改變。 集合類對象以此類推。 所以,
用@property聲明 NSString、NSArray、NSDictionary 經常使用copy關鍵字,是因爲他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作,爲確保對象中的字符串值不會無意間變動,應該在設置新屬性值時拷貝一份。
參考鏈接:iOS 集合的深複製與淺複製
3.讓自己的類用 copy 修飾符,重寫帶 copy 關鍵字的 setter
若想令自己所寫的對象具有拷貝功能,則需實現NSCopying協議。如果自定義的對象分爲可變版本與不可變版本,那麼就要同時實現NSCopyiog與NSMutableCopying協議。
具體步驟:
- 需聲明該類遵從NSCopying協議
-
實現NSCopying協議。該協議只有一個方法:
- (id)copyWithZone: (NSZone*) zone
注意:一提到讓自己的類用 copy 修飾符,我們總是想覆寫copy方法,其實真正需要實現的卻是“copyWithZone”方法。
以第一題的代碼爲例:
// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 修改完的代碼
typedef NS_ENUM(NSInteger, CYLSex) {
CYLSexMan,
CYLSexWoman
};
@interface CYLUser : NSObject<NSCopying>
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readonly) CYLSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
@end
然後實現協議中規定的方法:
- (id)copyWithZone:(NSZone *)zone {
CYLUser *copy = [[[self class] allocWithZone:zone]
initWithName:_name
age:_age
sex:_sex];
return copy;
}
但在實際的項目中,不可能這麼簡單,遇到更復雜一點,比如類對象中的數據結構可能並未在初始化方法中設置好,需要另行設置。舉個例子,假如CYLUser中含有一個數組,與其他CYLUser對象建立或解除朋友關係的那些方法都需要操作這個數組。那麼在這種情況下,你得把這個包含朋友對象的數組也一併拷貝過來。下面列出了實現此功能所需的全部代碼:
// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 以第一題《風格糾錯題》裏的代碼爲例
typedef NS_ENUM(NSInteger, CYLSex) {
CYLSexMan,
CYLSexWoman
};
@interface CYLUser : NSObject<NSCopying>
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readonly) CYLSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
- (void)addFriend:(CYLUser *)user;
- (void)removeFriend:(CYLUser *)user;
@end
// .m文件
// .m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
//
@implementation CYLUser {
NSMutableSet *_friends;
}
- (void)setName:(NSString *)name {
_name = [name copy];
}
- (instancetype)initWithName:(NSString *)name
age:(NSUInteger)age
sex:(CYLSex)sex {
if(self = [super init]) {
_name = [name copy];
_age = age;
_sex = sex;
_friends = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addFriend:(CYLUser *)user {
[_friends addObject:user];
}
- (void)removeFriend:(CYLUser *)user {
[_friends removeObject:person];
}
- (id)copyWithZone:(NSZone *)zone {
CYLUser *copy = [[[self class] allocWithZone:zone]
initWithName:_name
age:_age
sex:_sex];
copy->_friends = [[NSMutableSet alloc] initWithSet:_friends
copyItems:YES];
return copy;
}@end
4.@property 的本質:ivar、getter、setter
@property 的本質是什麼?
@property = ivar + getter + setter;
下面解釋下:
“屬性” (property)有兩大概念:ivar(實例變量)、存取方法(access method = getter + setter)。
“屬性” (property)作爲 Objective-C 的一項特性,主要的作用就在於封裝對象中的數據。 Objective-C 對象通常會把其所需要的數據保存爲各種實例變量。實例變量一般通過“存取方法”(access method)來訪問。其中,“獲取方法” (getter)用於讀取變量值,而“設置方法” (setter)用於寫入變量值。這個概念已經定型,並且經由“屬性”這一特性而成爲Objective-C
2.0
的一部分。 而在正規的 Objective-C 編碼風格中,存取方法有着嚴格的命名規範。 正因爲有了這種嚴格的命名規範,所以 Objective-C 這門語言才能根據名稱自動創建出存取方法。其實也可以把屬性當做一種關鍵字,其表示:
編譯器會自動寫出一套存取方法,用以訪問給定類型中具有給定名稱的變量。 所以你也可以這麼說:
@property = getter + setter;
例如下面這個類:
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
上述代碼寫出來的類與下面這種寫法等效:
@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
ivar、getter、setter 是如何生成並添加到這個類中的?
“自動合成”( autosynthesis)
完成屬性定義後,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”( autosynthesis)。需要強調的是,這個過程由編譯 器在編譯期執行,所以編輯器裏看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter、setter 之外,編譯器還要自動向類中添加適當類型的實例變量,並且在屬性名前面加下劃線,以此作爲實例變量的名字。在前例中,會生成兩個實例變量,其名稱分別爲 _firstName
與_lastName
。也可以在類的實現代碼裏通過
@synthesize語法來指定實例變量的名字.(關於@synthesize看第9點)
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
我爲了搞清屬性是怎麼實現的,曾經反編譯過相關的代碼,他大致生成了五個東西
OBJC_IVAR_$類名$屬性名稱
:該屬性的“偏移量” (offset),這個偏移量是“硬編碼” (hardcode),表示該變量距離存放對象的內存區域的起始地址有多遠。- setter與getter方法對應的實現函數
ivar_list
:成員變量列表method_list
:方法列表prop_list
:屬性列表
也就是說我們每次在增加一個屬性,系統都會在ivar_list
中添加一個成員變量的描述,在method_list
中增加setter與getter方法的描述,在屬性列表中增加一個屬性的描述,然後計算該屬性在對象中的偏移量,然後給出setter與getter方法對應的實現,在setter方法中從偏移量的位置開始賦值,在getter方法中從偏移量開始取值,爲了能夠讀取正確字節數,系統對象偏移量的指針類型進行了類型強轉.
5.@protocol 和 category 中使用 @property
- 在protocol中使用property只會生成setter和getter方法聲明,我們使用屬性的目的,是希望遵守我協議的對象能實現該屬性
-
category 使用 @property 也是隻會生成setter和getter方法的聲明,如果我們真的需要給category增加屬性的實現,需要藉助於運行時的兩個函數:
objc_setAssociatedObject
objc_getAssociatedObject
6.runtime 如何實現 weak 屬性
要實現weak屬性,首先要搞清楚weak屬性的特點:
weak 此特質表明該屬性定義了一種“非擁有關係” (nonowning relationship)。爲這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值。此特質同assign類似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。
那麼runtime如何實現weak變量的自動置nil?
runtime 對註冊的類, 會進行佈局,對於 weak 對象會放入一個 hash 表中。 用 weak 指向的對象內存地址作爲 key,當此對象的引用計數爲0的時候會 dealloc,假如 weak 指向的對象內存地址是a,那麼就會以a爲鍵, 在這個 weak 表中搜索,找到所有以a爲鍵的 weak 對象,從而設置爲 nil。
(注:在下文的《使用runtime Associate方法關聯的對象,需要在主對象dealloc的時候釋放麼?》裏給出的“對象的內存銷燬時間表”也提到__weak
引用的解除時間。)
我們可以設計一個函數(僞代碼)來表示上述機制:
objc_storeWeak(&a, b)
函數:
objc_storeWeak
函數把第二個參數--賦值對象(b)的內存地址作爲鍵值key,將第一個參數--weak修飾的屬性變量(a)的內存地址(&a)作爲value,註冊到 weak 表中。如果第二個參數(b)爲0(nil),那麼把變量(a)的內存地址(&a)從weak表中刪除,
你可以把objc_storeWeak(&a, b)
理解爲:objc_storeWeak(value,
key)
,並且當key變nil,將value置nil。
在b非nil時,a和b指向同一個內存地址,在b變nil時,a變nil。此時向a發送消息不會崩潰:在Objective-C中向nil發送消息是安全的。
而如果a是由assign修飾的,則: 在b非nil時,a和b指向同一個內存地址,在b變nil時,a還是指向該內存地址,變野指針。此時向a發送消息極易崩潰。
下面我們將基於objc_storeWeak(&a, b)
函數,使用僞代碼模擬“runtime如何實現weak屬性”:
// 使用僞代碼模擬:runtime如何實現weak屬性
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
id obj1;
objc_initWeak(&obj1, obj);
/*obj引用計數變爲0,變量作用域結束*/
objc_destroyWeak(&obj1);
下面對用到的兩個方法objc_initWeak
和objc_destroyWeak
做下解釋:
總體說來,作用是: 通過objc_initWeak
函數初始化“附有weak修飾符的變量(obj1)”,在變量作用域結束時通過objc_destoryWeak
函數釋放該變量(obj1)。
下面分別介紹下方法的內部實現:
objc_initWeak
函數的實現是這樣的:在將“附有weak修飾符的變量(obj1)”初始化爲0(nil)後,會將“賦值對象”(obj)作爲參數,調用objc_storeWeak
函數。
obj1 = 0;
obj_storeWeak(&obj1, obj);
也就是說:
weak 修飾的指針默認值是 nil (在Objective-C中向nil發送消息是安全的)
然後obj_destroyWeak
函數將0(nil)作爲參數,調用objc_storeWeak
函數。
objc_storeWeak(&obj1, 0);
前面的源代碼與下列源代碼相同。
// 使用僞代碼模擬:runtime如何實現weak屬性
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
/* ... obj的引用計數變爲0,被置nil ... */
objc_storeWeak(&obj1, 0);
objc_storeWeak
函數把第二個參數--賦值對象(obj)的內存地址作爲鍵值,將第一個參數--weak修飾的屬性變量(obj1)的內存地址註冊到 weak 表中。如果第二個參數(obj)爲0(nil),那麼把變量(obj1)的地址從weak表中刪除,在後面的相關一題會詳解。
使用僞代碼是爲了方便理解,下面我們“真槍實彈”地實現下:
如何讓不使用weak修飾的@property,擁有weak的效果。
我們從setter方法入手:
- (void)setObject:(NSObject *)object
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object = nil;
}];
}
也就是有兩個步驟:
-
在setter方法中做如下設置:
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
-
在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。做到這點,同樣要藉助runtime:
//要銷燬的目標對象 id objectToBeDeallocated; //可以理解爲一個“事件”:當上面的目標對象銷燬時,同時要發生的“事件”。 id objectWeWantToBeReleasedWhenThatHappens; objc_setAssociatedObject(objectToBeDeallocted, someUniqueKey, objectWeWantToBeReleasedWhenThatHappens, OBJC_ASSOCIATION_RETAIN);
知道了思路,我們就開始實現cyl_runAtDealloc
方法,實現過程分兩部分:
第一部分:創建一個類,可以理解爲一個“事件”:當目標對象銷燬時,同時要發生的“事件”。藉助block執行“事件”。
// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 這個類,可以理解爲一個“事件”:當目標對象銷燬時,同時要發生的“事件”。藉助block執行“事件”。
typedef void (^voidBlock)(void);
@interface CYLBlockExecutor : NSObject
- (id)initWithBlock:(voidBlock)block;
@end
// .m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 這個類,可以理解爲一個“事件”:當目標對象銷燬時,同時要發生的“事件”。藉助block執行“事件”。
#import "CYLBlockExecutor.h"
@interface CYLBlockExecutor() {
voidBlock _block;
}
@implementation CYLBlockExecutor
- (id)initWithBlock:(voidBlock)aBlock
{
self = [super init];
if (self) {
_block = [aBlock copy];
}
return self;
}
- (void)dealloc
{
_block ? _block() : nil;
}
@end
第二部分:核心代碼:利用runtime實現cyl_runAtDealloc
方法
// CYLNSObject+RunAtDealloc.h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 利用runtime實現cyl_runAtDealloc方法
#import "CYLBlockExecutor.h"
const void *runAtDeallocBlockKey = &runAtDeallocBlockKey;
@interface NSObject (CYLRunAtDealloc)
- (void)cyl_runAtDealloc:(voidBlock)block;
@end
// CYLNSObject+RunAtDealloc.m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 利用runtime實現cyl_runAtDealloc方法
#import "CYLNSObject+RunAtDealloc.h"
#import "CYLBlockExecutor.h"
@implementation NSObject (CYLRunAtDealloc)
- (void)cyl_runAtDealloc:(voidBlock)block
{
if (block) {
CYLBlockExecutor *executor = [[CYLBlockExecutor alloc] initWithBlock:block];
objc_setAssociatedObject(self,
runAtDeallocBlockKey,
executor,
OBJC_ASSOCIATION_RETAIN);
}
}
@end
使用方法: 導入
#import "CYLNSObject+RunAtDealloc.h"
然後就可以使用了:
NSObject *foo = [[NSObject alloc] init];
[foo cyl_runAtDealloc:^{
NSLog(@"正在釋放foo!");
}];
如果對cyl_runAtDealloc
的實現原理有興趣,可以看下這篇博文 Fun
With the Objective-C Runtime: Run Code at Deallocation of Any Object
7.ARC下,不顯式指定任何屬性關鍵字時,默認的關鍵字
-
對應基本數據類型默認關鍵字是
atomic,readwrite,assign
-
對於普通的OC對象
atomic,readwrite,strong
參考鏈接:
8. @property中有哪些屬性關鍵字?/ @property 後面可以有哪些修飾符?
屬性可以擁有的特質分爲四類:
-
原子性---
nonatomic
特質在默認情況下,由編譯器合成的方法會通過鎖定機制確保其原子性(atomicity)。如果屬性具備nonatomic特質,則不使用同步鎖。請注意,儘管沒有名爲“atomic”的特質(如果某屬性不具備nonatomic特質,那它就是“原子的” ( atomic) ),但是仍然可以在屬性特質中寫明這一點,編譯器不會報錯。若是自己定義存取方法,那麼就應該遵從與屬性特質相符的原子性。
-
讀/寫權限---
readwrite(讀寫)
、readooly (只讀)
- 內存管理語義---
assign
、strong
、weak
、unsafe_unretained
、copy
-
方法名---
getter=<name>
、setter=<name>
getter=<name>
的樣式:@property (nonatomic, getter=isOn) BOOL on;
(
setter=<name>
這種不常用,也不推薦使用。故不在這裏給出寫法。) - 不常用的:
nonnull
,null_resettable
,nullable
如果這樣聲明兩個屬性:
並定義
- @property (nonatomic, strong) NSString *string1;
- @property (nonatomic, unsafe_unretained) NSString *string2;
再來猜一下,下面的代碼會有什麼結果?
- self.string1 = @"String 1";
- self.string2 = self.string1;
- self.string1 = nil;
- NSLog(@"String 2 = %@", self.string2);
請注意,在此我並沒有叫你猜會有什麼輸出,因爲根本不會有輸出,你的程序會crash掉。
原因是什麼,其實就是野指針造成的,所以野指針是可怕的。爲何會造成野指針呢?同於用unsafe_unretained聲明的指針,由於self.string1=nil已將內存釋放掉了,但是string2並不知道已被釋放了,所以是野指針。然後訪問野指針的內存就造成crash. 所以儘量少用unsafe_unretained關鍵字。
在c/c++,objective-c內存管理中有一條是:誰分配誰釋放。 __autoreleasing則可以使對像延遲釋放。比如你想傳一個未初始化地對像引用到一個方法當中,在此方法中實始化此對像,那麼這種情況將是__autoreleasing表演的時候。看個示例:
- - (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{
- NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];
- NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];
- NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
- *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
- }
- -(void)test
- {
- NSError *error = nil;
- [self generateErrorInVariable:&error];
- NSLog(@"Error = %@", error);
- }
這樣即便在函數內部申請的空間,在函數外部也可以使用,同樣也適合誰分配誰釋放的原則。
同樣下面的代碼也是類似原因, 只不過在沒有開啓ARC的情況下適用:
- -(NSString *)stringTest
- {
- NSString *retStr = [NSString stringWithString:@"test"];
- return [[retStr retain] autorelease];
- }
開啓ARC後,應改爲:
- -(NSString *)stringTest
- {
- __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];
- return retStr;
- }
9.@synthesize和@dynamic
9.1分別的作用
- @property有兩個對應的詞,一個是@synthesize,一個是@dynamic。如果@synthesize和@dynamic都沒寫,那麼默認的就是
@syntheszie var = _var;
- @synthesize的語義是如果你沒有手動實現setter方法和getter方法,那麼編譯器會自動爲你加上這兩個方法。
- @dynamic告訴編譯器:屬性的setter與getter方法由用戶自己實現,不自動生成。(當然對於readonly的屬性只需提供getter即可)。假如一個屬性被聲明爲@dynamic var,然後你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當程序運行到
instance.var = someVar
,由於缺setter方法會導致程序崩潰;或者當運行到someVar = var
時,由於缺getter方法同樣會導致崩潰。編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。
9.2@synthesize合成實例變量的規則,有以下幾點:
-
如果指定了成員變量的名稱,會生成一個指定的名稱的成員變量,
-
如果這個成員已經存在了就不再生成了.
-
如果是
@synthesize foo;
還會生成一個名稱爲foo的成員變量,也就是說:如果沒有指定成員變量的名稱會自動生成一個屬性同名的成員變量,
-
如果是
@synthesize foo = _foo;
就不會生成成員變量了.
@interface TestClass : NSObject
{
NSObject * _object;
}
@property (nonatomic,retain)NSObject *object;
@property (nonatomic,retain)NSObject *_object;
@end
Xcode會提示:Auto property synthesis will not synthesizeproperty
'_object' because it cannot share an ivar with another synthesized property9.3不會autosynthesis(自動合成)的情況:
- 同時重寫了setter和getter時
- 重寫了只讀屬性的getter時
- 使用了@dynamic時
- 在 @protocol 中定義的所有屬性
- 在 category 中定義的所有屬性
-
重載的屬性
當你在子類中重載了父類中的屬性,你必須 使用
@synthesize
來手動合成ivar。
@synthesize
來手動合成ivar。@import Foundation;
@interface CYLObject : NSObject
@property (nonatomic, copy) NSString *title;
@end
@implementation CYLObject {
// NSString *_title;
}
//@synthesize title = _title;
- (instancetype)init
{
self = [super init];
if (self) {
_title = @"微博@iOS程序犭袁";
}
return self;
}
- (NSString *)title {
return _title;
}
- (void)setTitle:(NSString *)title {
_title = [title copy];
}
@end
當你同時重寫了setter和getter時,系統就不會生成ivar(實例變量/成員變量)。這時候有兩種選擇:
- 要麼如第14行:手動創建ivar
- 要麼如第17行:使用
@synthesize foo = _foo;
,關聯@property與ivar。
更多信息,請戳- 》 When should I use @synthesize explicitly?