iOS开发中由属性(property)引发的坑

copy修饰的NSMutableArray属性(property)初始化问题

对于属性:

@property (nonatomic, copy) NSMutableArray *someArray;

若初始化时使用self.someArray:

self.someArray = [[NSMutableArray alloc] initWithCapacity:200];

当使用:

[self.someArray addObject:name];

APP Crash,其中关键 Error Info如下:

-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x7f9c89701c20

原因是,通过copy修饰的property,若通过self.someArray =来赋值初始化,则是通过系统合成setter方法实现,由于设置copy修饰词,则返回实际上是不可变数组(NSArray),当调用addObject 方法会报错。

初始化 或者 赋值 部分,做如下修改:

_someArray = [[NSMutableArray alloc] initWithCapacity:200];

则APP运行正常,原因是:
_someArray是实例变量,实例变量并没有 copy 修饰,指向的仍是定义的 NSMutableArray 类型。所以即使后面通过 self.someArray 使用 addObject方法仍然可行,因为初始化赋值阶段获取的是NSMutableArray类型对象

知道最佳解决方案么? 其实,就是把copy修饰词改为strong,因为可变数组对象是个容器,只要其元素中没有和self对象存在相互持有造成内存泄漏的,则不会出现任何问题。

@synthesize @dynamic 正确使用

在之前的文章 Objective-C 2.0 基础要点归纳 中,对属性(property)进行了分析,但没有对关键字 @property 和 @synthesize 以及@dynamic的作用进行对比解析,这部分将对之进行详细分析,但问题却是因 atomic 修饰词引出。

关键字的作用说明

  • @property 预编译命令(关键字)作用是声明属性的 getter和setter方法
  • @synthesize 生成属性访问器即 getter和setter方法
  • @dynamic 禁止编译器生成 getter/setter,通过自定义或者在运行时动态创建绑定:主要使用在Core Data中

atomic 是操作原子性,作为属性修饰词,则编译器生产的 getter/setter方法中存在 @synchronized(self)或者lock锁机制只允许原子性访问。

若只自定义一个属性访问器(setter/getter中一个),会出现warning:writable atomic property ‘name’ cannot pair a synthesized getter with …

若两个都定义,则需要 @synthesize name = _name; 两个都自定义的情况下,系统不会合成属性访问器,则此时需要显示调用 @synthesize来定义属性的实例变量名,以便使用属性的实例变量

实践代码:

TestAtomic 类

TestAtomic.h

#import <Foundation/Foundation.h>

@interface TestAtomic : NSObject

@property (atomic, copy) NSString *name;// 注意atomic修饰符

@end

TestAutomic.m

#import "TestAtomic.h"
@implementation TestAtomic

@synthesize name = _name;

- (NSString *)name
{
    if (![_name isEqualToString:@""]) {
        return _name;
    } else {
        return nil;
    }
}

- (void)setName:(NSString *)name
{
    @synchronized(self) {      //类似于默认setter方法
        if (![_name isEqualToString:name]) {
            _name = name;
        }
    }
}

@end

TestB 类,继承TestAtomic类
TestB.h

#import "TestAtomic.h"

@interface TestB : TestAtomic

@property (atomic, copy) NSString *Bname;

- (instancetype)initWithSuperName;

- (void)print;
@end

TestB.m

#import "TestB.h"
@implementation TestB

- (instancetype)initWithSuperName
{
    self = [super init];
    if (self) {
        [self setName:@"this is A"];
    }

    return self;
}


- (void)print
{
    NSLog(@"%@ --- %@", self.Bname, self.name);
}

@end

Main 函数:

#import <Foundation/Foundation.h>
#include "TestAtomic.h"
#include "TestB.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        TestB *testB = [TestB new];
        testB.name = @"this is good";

        //testB = [testB initWithSuperName];
        testB.Bname = @"this is B";
        [testB print];
    }
return 0;
}

参考资源

NSMutableArray问题

error: writable atomic property cannot pair a synthesized setter/getter

iphone 开发中属性 property 和 synthesize 权威的介绍

iOS中atomic的实现解析

@synthesize和@dynamic区别

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页