TheClass *newObject = [[TheClass alloc] init];
Do not overrideallocto include initialization code. Instead, implement class-specific versions ofinit...methods.
For historical reasons,allocinvokesallocWithZone:.
結論:alloc方法必須與init方法合用,來完成初始化過程。不要復寫alloc方法,可以復寫init方法來實現特別的需求。由於歷史原因,alloc會調用方法+allocWithZone: +allocWithZone: 使用方法:You must use aninit...method to complete the initialization process. For example:
Code Listing 3TheClass *newObject = [[TheClass allocWithZone:nil] init];
Do not overrideallocWithZone:to include any initialization code. Instead, class-specific versions ofinit...methods.
This method exists for historical reasons; memory zones are no longer used by Objective-C.
與alloc方法類似,此方法也需要與init方法合用。內存空間不再被oc使用,所以zone參數傳nil即可。 -init Implemented by subclasses to initialize a new object (the receiver) immediately after memory for it has been allocated. 由子類實現該方法,在得到內存分配之後立即初始化一個新的對象。 使用方法: Aninitmessage is coupled with analloc(orallocWithZone:) message in the same line of code: 與alloc或者allocWithZone組合使用。 如果要復寫,格式如下:
- (instancetype)init {
self = [super init];
if (self) {
// Initialize self
}
return self;
}
-copy
Returns the object returned bycopyWithZone:.
該方法的返回值是從copyWithZone:返回的
+copyWithZone:
This method exists so class objects can be used in situations where you need an object that conforms to theNSCopyingprotocol. For example, this method lets you use a class object as a key to anNSDictionaryobject. You should not override this method.
只有准守了NSCopying協議的類對象,才能調用該方法。例如,使用該方法可以將類對象作為字典對象的key。不能override。
-mutableCopy
Returns the object returned bymutableCopyWithZone:where the zone isnil.
與 -copy 同理
+mutableCopyWithZone:
This method exists so class objects can be used in situations where you need an object that conforms to theNSMutableCopyingprotocol. For example, this method lets you use a class object as a key to anNSDictionaryobject. You should not override this method.
與 -copyWithZone:同理。
+new
This method is a combination ofallocandinit. Likealloc, it initializes theisainstance variable of the new object so it points to the class data structure. It then invokes theinitmethod to complete the initialization process.
該方法是alloc和init兩個方法的結合版。和alloc方法類似,它初始化新對象的isa指針(指向類數據結構)實例變量。然後自動調用init方法來完成該實例化過程。
綜上所述
單例的初始化只要保證,- alloc -init 、-new、-copy、mutableCopy、以上四個方法創建出來的對象是同一個就ok了。
Demo 代碼如下
#import@interface SingleInstance : NSObject + (instancetype)shareInstance; @property (nonatomic ,assign) NSInteger factor1; //測試用 @end
#import "SingleInstance.h"
@implementation SingleInstance
static SingleInstance *instance = nil;
+ (instancetype)shareInstance
{
static SingleInstance *instance;
static dispatch_once_t onceToken;
//dispatch_once (If called simultaneously from multiple threads, this function waits synchronously until the block has completed. 由官方解釋,該函數是線程安全的)
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
//保證從-alloc-init和-new方法返回的對象是由shareInstance返回的
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
return [SingleInstance shareInstance];
}
//保證從copy獲取的對象是由shareInstance返回的
- (id)copyWithZone:(struct _NSZone *)zone
{
return [SingleInstance shareInstance];
}
//保證從mutableCopy獲取的對象是由shareInstance返回的
- (id)mutableCopyWithZone:(struct _NSZone *)zone
{
return [SingleInstance shareInstance];
}
@end
以上:保證了無論用何種方式獲取單例對象,獲取到的都是同一個對象。
驗證代碼如下
SingleInstance *single1 = [[SingleInstance alloc] init];
SingleInstance *single2 = [SingleInstance new];
SingleInstance *single3 = [SingleInstance shareInstance];
SingleInstance *single4 = [single1 copy];//調用此方法獲取對象,需要該類重寫過copyWithZone:方法,不然會crash
SingleInstance *single5 = [single1 mutableCopy];//需重寫mutableCopyWithZone:,否則crash
single1.factor1 = 1;
single2.factor1 = 2;
single3.factor1 = 3;
single4.factor1 = 4;
single5.factor1 = 5;
NSLog(@"s1 value = %ld \n",single1.factor1);
NSLog(@"s2 value = %ld \n",single2.factor1);
NSLog(@"s3 value = %ld \n",single3.factor1);
NSLog(@"s4 value = %ld \n",single4.factor1);
NSLog(@"s5 value = %ld \n",single5.factor1);
NSLog(@"memory address \n %@ \n %@ \n %@ \n %@ \n %@",single1,single2,single3,single4,single5);
控制台打印結果為:
ttt[44738:1478271] s1 value = 5
//開始模擬
- (void)simulateStart {
SingleInstance *single1 = [[SingleInstance alloc] init];
single1.factor1 = 10;//總共有10張票
[self performSelectorInBackground:@selector(sellTickets) withObject:nil];
[self performSelectorInBackground:@selector(sellTickets) withObject:nil];
[self performSelectorInBackground:@selector(sellTickets) withObject:nil];
}
- (void)sellTickets
{
SingleInstance *single1 = [SingleInstance shareInstance];
NSThread *thread = [NSThread currentThread];
thread.name = [NSString stringWithFormat:@"%.6f",[[NSDate date] timeIntervalSince1970]];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
//檢查票數
for (int i = 0; i<10; i++) {
NSInteger leftTicketsCount = single1.factor1;
if (leftTicketsCount <= 0) {
NSLog(@"賣光了 \n");
break;
}
else
{
[NSThread sleepForTimeInterval:0.02];
NSInteger remain = single1.factor1;
single1.factor1--;
NSInteger left = single1.factor1;
NSLog(@"線程名:%@ 余票數 %ld , 賣出一張 , 剩余票數 %ld \n",thread.name,remain,left);
}
}
});
}
控制台打印的結果為:
線程名:1484826739.634151余票數 9 ,賣出一張 ,剩余票數 8
- (void)sellTickets
{
SingleInstance *single1 = [SingleInstance shareInstance];
NSThread *thread = [NSThread currentThread];
thread.name = [NSString stringWithFormat:@"%.6f",[[NSDate date] timeIntervalSince1970]];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
//注意一定要把線程對數據操作的代碼放在同步鎖的花括號裡面
@synchronized (single1) {
//檢查票數
for (int i = 0; i<11; i++) {
NSInteger leftTicketsCount = single1.factor1;
if (leftTicketsCount <= 0) {
NSLog(@"賣光了 \n");
break;
}
else
{
[NSThread sleepForTimeInterval:0.02];
NSInteger remain = single1.factor1;
single1.factor1--;
NSInteger left = single1.factor1;
NSLog(@"線程名:%@ 余票數 %ld , 賣出一張 , 剩余票數 %ld \n",thread.name,remain,left);
}
}
}
});
}
控制台打印數據為:
線程名:1484827607.308329余票數 10 ,賣出一張 ,剩余票數 9