带ARC的单身人士

时间:2011-11-03 15:34:14

标签: objective-c singleton automatic-ref-counting

我的问题如下:我有一个单例类型的对象(我正在使用ARC)在实现文件中有这个代码

+(id)sharedInstance 
{
    static DataManager *sharedInstance;
    if (sharedInstance == nil) {
        sharedInstance = [[DataManager alloc] init];
    }
    return sharedInstance;
}

+(NSManagedObjectContext *)getManagedContext
{
    AppDelegate *applicationDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
    return [applicationDelegate managedObjectContext];
}

+(void)saveContext:(NSManagedObjectContext *)context
{
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }
}

#pragma mark - Data management methods

+(void)addPersonWithName:(NSString *)name andPicture:(UIImage *)picture
{
    NSManagedObjectContext *context = [self getManagedContext]; //no problem here
    //some code 
    [self saveContex:context]; // no known class method for selector saveContext:
}

为什么?该方法在.h文件中声明为+ ... getManagedContext模型没有给出此错误????

1 个答案:

答案 0 :(得分:61)

方法中的关键字self引用方法的所有者,该方法是实例对象的实例,以及类方法的类。但是,消息saveContex在最后(saveContext)缺少了一个。

dispatch_once singleton

这是一个与ARC兼容的更好的单身成语:

+(MySingleton *)sharedInstance {
    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
    });
    return shared;
}

与Xcode模板相同的代码

与带有占位符的Xcode模板相同的代码:

+ (<#class#> *)shared<#name#> {
    static dispatch_once_t onceToken;
    static <#class#> *shared<#name#> = nil;
    dispatch_once(&onceToken, ^{
        shared<#name#> = <#initializer#>;
    });
    return shared<#name#>;
}

相同代码+禁用alloc / init / new

想要告诉用户他们应该调用sharedInstance而不是alloc / init / new?您可以禁用属性不可用的方法。如果在类上调用任何这些方法,这将导致编译器错误。

#import <Foundation/Foundation.h>

@interface MySingleton : NSObject

+(instancetype) sharedInstance;

// clue for improper use (produces compile time error)
+(instancetype) alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype) init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype) new __attribute__((unavailable("new not available, call sharedInstance instead")));

@end

#import "MySingleton.h"

@implementation MySingleton

+(instancetype) sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype) initUniqueInstance {
    return [super init];
}

@end

警告:dispatch_once不可重入

请勿在{{1​​}}阻止内对sharedInstance进行递归调用。

如果从多个线程中调用dispatch_once,它将成为阻止并发访问的障碍。 但是如果你在块内部的同一个线程中再次调用它,它将使线程死锁。这个例子说明了这个问题:

dispatch_once

+初始化单身

使用+initialize是创建单身人士的另一种习惯用法。优点:它比#import <Foundation/Foundation.h> static NSRecursiveLock *_lock = nil; // constructor = run before main. used = emit code even if the function is not referenced. // See http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html static void runBeforeMain(void) __attribute__ ((constructor, used)); static void runBeforeMain(void) { _lock = [NSRecursiveLock new]; } static void test(void) { static NSUInteger count = 0; NSLog(@"iteration #%lu", ++count); // WRONG: deadlock! //static dispatch_once_t token; //dispatch_once(&token, ^{ // test(); //}); // OK [_lock lock]; test(); [_lock unlock]; --count; } int main(int argc, char **argv) { @autoreleasepool { test(); } return EXIT_SUCCESS; } 快几倍。缺点:每个类调用一次dispatch_once,因此如果您对单例进行子类化,也会为每个父类创建一个实例。只有在您知道单例不会被子类化时才使用它。

+initialize