iOS 基础02--单例、属性修饰符、深浅拷贝

iOS 基础02—单例、属性修饰符、深浅拷贝

单例

讲单例就必须得先讲讲单例这种设计模式的作用和最可能出现的应用场景,以便更好地理解这种设计模式:
比如在整个进程当中,我们经常会用到用户信息,这就要求我们能够把用户信息存在一个统一的对象当中,以便于对信息进行操作。
有对情况下,某个类也可能只能允许只有一个实例。比如音频播放器。

这样,我们就大概了解单例的了,它就是整个进程只存在一个实例对象的类。所以它的生命周期也就是进程的生命周期。只要app不被干掉,实例对象就不会被释放。

创建单例是最常考的面试题

  • GCD 创建方法:
    GCD once 内部是会加锁,但是比普通的互斥锁 效率高 100多倍,所以苹果推荐使用once
#import "ShareManager.h"
static ShareManager * _shareInstance;

@implementation ShareManager
+ (instancetype)shareinstance {

    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        _shareInstance = [[self alloc] init];
    });
    return _shareInstance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        _shareInstance = [super allocWithZone:zone];
    });
    return _shareInstance;
}

-(id)copyWithZone:(NSZone *)zone{
    return _shareInstance;
}

-(id)mutableCopyWithZone:(NSZone *)zone{
    return _shareInstance;
}

@end
  • 互斥锁@synchronized方法:
static ShareManager * _shareInstance;
@implementation ShareManager
+ (instancetype)shareinstance {

    @synchronized (self) {
        if (_shareInstance == nil) {
            _shareInstance = [[self alloc] init];
        }
    }
    return _shareInstance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    @synchronized (self) {
        if (_shareInstance == nil) {
            _shareInstance = [super allocWithZone:zone];
        }
    }
    return _shareInstance;
}

-(id)copyWithZone:(NSZone *)zone{
    return _shareInstance;
}

-(id)mutableCopyWithZone:(NSZone *)zone{
    return _shareInstance;
}

@end

这里要着重说明一下:如果没有实现allocWithZone:(struct _NSZone *)zone这个类方法,那么调用[[ alloc]init]创建出来的对象就不是shareinstance的实例了,这样一个项目就有可能出现多个实例;如果不写copy或multablecopy,则单例就无法使用[ copy]或[ multablecopy]这两个函数了。

单例的删除:

static TestSingle * _instance;
static dispatch_once_t oneToken; //注意这个令牌要作为全局变量来处理,在删除操作的时候将其置为0。
@implementation TestSingle
+ (instancetype)shareinstance {
    dispatch_once(&oneToken, ^{
        _instance = [TestSingle new];
    });
    return _instance;
}

+ (void)deleteSingle {
    oneToken = 0; //在删除操作的时候将其置为0。
    _instance = nil;
}
- (void)dealloc
{
    printf("TestSingle has been destroyed");
}

弱单例,其生命不是存在于整个项目的生命周期,而是在所有引用它的类都释放后,会随着释放的单例:

@implementation TestWeakSingle

+ (instancetype)shareInstance {
    static __weak TestWeakSingle * _instance;
    TestWeakSingle * strongInstance = _instance;
    @synchronized(self){
        strongInstance = [TestWeakSingle new];
        _instance = strongInstance;
    }
    return strongInstance;
}

- (void)dealloc
{
    printf("TestSingle has been destroyed");
}

@property:

@property 就相当于帮我们创建了getter和setter方法,这样我们就不需要自己另外写代码。

    @property = ivar(实例变量)+ getter + setter;
  • atomic&nonatomic:
    atomic:原子性——要么完整被执行,要么不执行,这样在有无数个线程同时访问它的时候,它会保证有且只有一个线程在访问它,而且一定会返回一个完整的值(其实就是不会返回垃圾地址),然后才会允许其他线程对它进行访问。但是其实它并不能保证线程安全,比如有多个线程在排队对A进行getter或setter方法,这个没有问题,但是如果此时还有一个线程对A进行了release操作还是会crash的。概括来说atomic只保证了getter和setter的完整性,也就是说,这个属性的读\写是线程安全的,但是release并不受getter和setter的限制,这就意味着它不不是线程安全的。

nonatomic:也就是非原子性的,它虽然无法保证getter和setter的完整性,但是它保证了无论对属性进行什么操作都是可以进行的,虽然结果无法预料。 接着用代码来实现一下:

@interface ViewController ()
@property (atomic,copy)NSString * str;
@property (nonatomic,copy)NSString * str2;
@end

@implementation ViewController
//OC中定义属性,通常会声明一个_成员变量(这个功能是Xcode的功能),如果你同时重写了属性的getter&setter方法,_成员变量就不会自动生成。
@synthesize str = _str;
@synthesize str2 = _str2; //如果要同时实现getter和setter方法,必须实现@synthesize

-(void)setStr:(NSString *)str{
    @synchronized (self) {
       _str = str;
    }
}

-(NSString *)str{
    NSString * tempStr = nil;
    @synchronized (self) {
        tempStr = _str;
    }
    return tempStr;
}

-(void)setStr2:(NSString *)str2{
     _str2 = str2;
}

- (NSString *)str2{
    return _str2;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.str = @"123";
    NSLog(@"%@",self.str);

    self.str2 = @"456";
    NSLog(@"%@",self.str2);
}

如上代码,atomic的属性,如果我们自己重写getter和setter方法是需要加互斥锁的,而加锁对于iOS来说是很消耗性能的,所以我们在不考虑多线程的大多数情况下都是使用nonatomic的,它能够提高更高的性能和效率。(这里稍微说一下,atomic属性的内部就有一把锁:自旋锁。自旋锁和互斥锁的共同点就是都能保证同一时间点就只有一条线程访问,不同点就是互斥锁锁上时,在外等待的线程会进入睡眠状态,当锁打开时,休眠的线程又会被重新唤醒。而自旋锁锁上时,就绪线程就会使用死循环的方式一直等待锁定的代码执行完毕,自旋锁更适合执行非常短的代码,但是无论什么锁,都是以CPU性能作为代价的)。然后atomic内部实现只有在setter方法中才会加锁,在getter方法中不会加锁,也就是说写操作的时候可以多线程,读操作如果多线程就会产生脏数据。

assign & weak & unsafe_unretained & strong & copy

  • assign:对属性进行简单的赋值操作,不影响新值的引用计数,也不改变旧值的引用计数。主要应用于NSInteger这些标量类型,此外,它同样可以作用于对象类型,如NSString。但是作用于对象类型时,如果对象被释放了,指针仍会指向之前被销毁的内存,这时访问该属性就会产生野指针并crash。
@property (nonatomic, assign) NSString * nick;
FCFPerson * p = [[FCFPerson alloc]init];

NSMutableString * s = [[NSMutableString alloc] initWithString:@"fcf"]; //这里使用NSMutableString而不使用NSString,是因为NSString会缓存字符串,当执行s=nil时,实际上还是没有被销毁。。
p.nick = s;

NSLog(@"nick:%p",p.nick);
NSLog(@"s:%p",s);

s = nil;

NSLog(@"nick:%p",p.nick); //这里crash

NSLog(@"%@",p.nick);
  • weak:同样对属性进行简单的赋值操作,不影响新值的引用计数,也不改变旧值的引用计数。但是它只能用于修饰对象类型。其次,当对象被释放了,指针会自动赋值为nil,这样就可以避免野指针问题了。
  • unsafe_unretained:这个基本就和assign相同,不影响新值的引用计数,也不改变旧值的引用计数,而且对象被释放的时候,也会产生野指针。它与assign唯一的区别在于它和weak一样只能修饰对象。
  • strong:对属性所赋的值持有强引用,会先增加新值的引用计数,然后再释放对象减少旧值的引用计数。但是,它只能修饰对象类型
  • copy:它也只能修饰对象类型。它会在内存里拷贝一份对象,两个指针指向不同的内存地址。一般来修饰有对应可变类型子类的对象。比如NSString(子类NSMutableString),如果使用strong修饰NSString,那么修改了所赋对象的值,str也会随之改变:
@interface FCFPerson : NSObject
@property (nonatomic, strong) NSString * phone;
@end

FCFPerson * p = [[FCFPerson alloc]init];

NSMutableString * s = [[NSMutableString alloc] initWithString:@"123"];
p.phone = s;

NSLog(@"phone:%p,    phone:%@",p.phone,p.phone);
NSLog(@"s:%p,    s:%@",s,s);

[s appendString:@"456"];
NSLog(@"phone:%p,    phone:%@",p.phone,p.phone);

NSLog(@"s:%p,    s:%@",s,s);
//结果:
phone:0x17426c280,    phone:123
s:0x17426c280,    s:123
phone:0x17426c280,    phone:123456
s:0x17426c280,    s:123456

其次,当使用copy为关键字之后,调用setter方法后,拷贝的对象就变成不可变的了,如果用copy去修饰NSMutableArray、NSMutableString、NSMutableDictionary,那么当它调用addObject类似可变对象的方法时就会奔溃。

property的深入理解

使用命令:

clang -rewrite-objc main.m

将项目中main.m转成main.cpp文件


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值