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區別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章