`
lizaochengwen
  • 浏览: 662626 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

allocwithzone

阅读更多
首 先,从copy开始说,简而言之,copy的目的就是生成一个新的实例,然后把其成员都按原实例赋值。对于非指针型的成员,比如BOOL, int, float,这样的赋值可以直接进行。但是对于指针型的数据,比如Objc中用到的对象,就有Deep Copy和Shallow Copy的区别——这个和在C++中的基本上是一样的:是生成新的成员对象,或是指向同一成员对象。

了 解了这点以后,再看看Copy在Objetive-C中的实现方式。如果要调用一个对象的copy方法,这个对象必须遵循NSCopying的协议。这个 协议中规定了一个方法:- (id)copyWithZone:(NSZone *)zone;我们就是通过实现这个方法给对象提供拷贝的功能。对于很多现有类,如NSString,NSDictionary,。。。这个方法已经实 现。假设我们现在自定义了一个类,需要为这个类提供拷贝的功能,就需要自己来动手写CopyWithZone的方法:示例如下:

这个是自定义的类:
@interface Product : NSObject <NSCopying>
{
NSString *productName;
float price;
id delegate;
}

@end

然后我们需要在Product类中实现NSCopying中的copyWithZone方法:
- (id)copyWithZone:(NSZone *)zone
{
Product *copy = [[[self class] allocWithZone: zone]
initWithProductName:[self productName]
price:[self price]]; //注意这里,我们使用了class的allocWithZone的方法创建了一个拷贝,这里假定Product类中有一个 initWithProductName: price:的初始化方法。那么这样调用后就得到了一个Product的副本,而且name和price都已经设置好了

[copy setDelegate:[self delegate]]; //这里再设置delegate

return copy; //返回副本
}

那么这样,如果我们有一个product的实例, 假设为product1,然后调用Product *product2 = [product1 copy];
就会使用我们上面写的copyWithZone的方法创建一个product1的副本,然后赋值给product2。

这里再以上面方法中的成员delegate为例,解释一下deep copy和shallow copy:

在 copyWithZone方法中,我们得到了一个新的product实例,但是delegate是个对象,所以在副本中,我们可以选择创建一个新的 delegate对象(deep copy),或是指向同一个delegate(shallow copy)。这个就取决于Product类中的setDelegate:方法了。你可以选择在setDelegate的时候,copy,也可以让它们都指 向同一个对象(但是需要retain,原因可以自己思考一下),当然,简单assign在某种情况下也是可以的。

假设在Product类中有setDelegate:方法,或是有delegate的property:
- (void)setDelegate: (id)aDelegate
{
[delegate release];
delegate = [delegate copy];
}

这 样就是一个深拷贝了,因为使用了delegate的copy方法得到了一个delegate的副本。至于如何得到delegate的副本,就要看 delegate的copyWithZone方法的实现了,不在这个层面的考虑中。也就是说,copy总是一中“递归”的形式,从上到下,我们可以一层一 层的考虑。

- (void)setDelegate: (id)aDelegate
{
[delegate release];
delegate = [aDelegate retain];
}
这样操作后,delegate和aDelegate为同一对象,但是为了内存管理方面的要求,我们调用了retain来将reference count加了一。当然,如果不需要了,还可以直接赋值(assign):
- (void)setDelegate: (id)aDelegate
{
delegate = aDelegate
}

你可以把这个例子自己实现一下,然后用log打一打内存,这个结构就很明了了。

然后再说一下可变副本(mutable copy)和不可变副本(immutable copy):
可变和不可变的概念,我们之前通过NSDictionary和NSMutableDictionary的区别了解过。
一 般来说,如果我们的某个类需要区别对待这两个功能——同时提供创建可变副本和不可变副本的话,一般在NSCopying协议规定的方法 copyWithZone中返回不可变副本;而在NSMutableCopying的mutableCopyWithZone方法中返回可变副本。然后调 用对象的copy和mutableCopy方法来得到副本。

举个例子:
NSDictionary类已经遵循了NSCopying和NSMutableCopy的协议,也就是说我们可以调用它的copy和mutableCopy来得到不可变和可变的副本,程序如下:

NSDictionary *testDict = [[NSDictionary alloc]initWithObjectsAndKeys:@"hello", @"test",nil];
NSDictionary *destDict = [testDict copy];
NSLog(@"test Dict:%p,retain Count: %d\ndest Dict:%p, retain Count: %d",testDict,[testDict retainCount],destDict,[destDict retainCount]);

这个在我机器上的运行结果为:
test Dict:0x11f220, retain Count: 2
dest Dict:0x11f220,retain Count: 2
看 起来,两个dict指向了同一片内存区域,但是retainCount加了1。这点需要理解一下,因为我们使用NSCopying方法要返回一个不可变对 象。而且原来的testDict也是不可变的,那么这里的“副本”也就没多大意义了(这就如同使用字符串常量时,系统会为我们优化,声明了多个字符串,但 是都是常量,且内容相等,那么系统就只为我们申请一块空间,这个道理是一样的)。既然都不可变,那么指向同一个空间就可以了。这里的copy和 retain没什么区别。

我们使用copyWithZone的方法返回immutable的对象,而不管原来的是可变的或是不可变的。我们再看一下如下代码:
NSMutableDictionary *testDict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:@"hello", @"test",nil];
NSMutableDictionary *destDict = [testDict copy];
NSLog(@"test Dict:%p,retain count:%d\ndest Dict:%p,retain count:%d",testDict,[testDict retainCount],destDict,[destDict retainCount]);
[destDict setObject:@"what" forKey:@"test2"];

NSMutableDictionary是可变的,该代码在我机器上运行的结果为:
test Dict:0x20dcc0,retain count:1
dest Dict:0x209120,retain count:1
*** -[NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
可 以看到因为我们调用了可变对象的copy方法,这个不像之前的例子中一样,只是retain了一下。这里的test dict和dest Dict已经是两个对象了,但是,copyWithZone的方法返回的是不可变的对象,因此之后的setObject: forKey:方法会出现错误。

下面这样改一下就OK了。

NSMutableDictionary *testDict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:@"hello", @"test",nil];
NSMutableDictionary *destDict = [testDict mutableCopy];
NSLog(@"test Dict:%p,retain count:%d\ndest Dict:%p,retain count:%d",testDict,[testDict retainCount],destDict,[destDict retainCount]);
[destDict setObject:@"what" forKey:@"test2"];
NSLog(@"destDict:%@",destDict);
运行结果为:
test Dict:0x123550,retain count:1
dest Dict:0x10a460,retain count:1

destDict:{
test = hello;
test2 = what;
因为我们使用了mutableCopy来得到了一个可变副本。

Note:对于系统提供的所有既支持NSCopying,又支持NSMutableCopying的类。
copy方法,得到的是不可变对象,不管以前的是可变还是不可变。
mutableCopy方法,得到的是可变对象,不管以前的是可变还是不可变。
原文地址:http://blog.csdn.net/qingqichiyu/article/details/6718233
分享到:
评论

相关推荐

    Iphone面试题

    6. 写一个NSString类的实现 ... obj = [self allocWithZone: NSDefaultMallocZone()];
 obj = [obj initWithCString: nullTerminatedCString encoding: encoding];
 return AUTORELEASE(obj);
}

    iOS单例代码

    _sharedInstance = [super allocWithZone:zone]; }); return _sharedInstance; } - (id)copyWithZone:(NSZone *)zone { return _sharedInstance; } @end ``` 在Swift中,实现单例的常见方式是使用`GCD`的`...

    细究单例那些你不知道的事(OC).zip

    然而,仅仅这样还不够,为了防止其他对象通过`alloc-init`方式创建新的实例,我们需要重写`allocWithZone:`方法: ```objc - (instancetype)allocWithZone:(struct _NSZone *)zone { return [SingletonClass ...

    iOS开发中的几种设计模式介绍

    为了保证单例,需要控制类的构造方式,例如在Objective-C中重写`allocWithZone:`方法。单例模式简化了资源的访问和管理,但需谨慎使用,避免过度依赖和不必要的全局状态。 5. **策略模式**: 策略模式允许在运行时...

    设计模式ForiOS-02-单例模式.pdf

    sharedInstance = [[super allocWithZone:NULL] init]; } return sharedInstance; } - (id)init { self = [super init]; if (self) { // 初始化操作 } return self; } @end ``` 2) **ARC + GCD**: 在ARC...

    iOS面试题及答案.doc

    这段代码首先通过`allocWithZone:`方法为新对象分配内存,然后调用`initWithCString:encoding:`初始化方法设置字符串内容,最后返回autorelease的对象,确保对象在不再使用时会被正确地释放。 2. `static` 关键字的...

    IOS设计模式浅析之原型模式(Prototype)--copy - iOS知识库1

    Prototype *copy = [[Prototype allocWithZone:zone] init]; copy.name = [self.name copyWithZone:zone]; // 对name属性进行深复制 return copy; } @end ``` 在这个例子中,`Prototype`类实现了`NSCopying`协议...

    ios开发之iOSSocket开发.pdf

    3. `allocWithZone:`方法被重写,确保在尝试分配新实例时,如果单例已经存在,返回现有的单例,否则创建新的实例。 4. `copyWithZone:`、`retain`和`retainCount`方法也被重写,以确保单例不会被复制或释放,保持其...

    史上最全的iOS面试题及答案

    2. **内部实现**:在内部,首先通过`allocWithZone:`分配内存来创建一个新的`NSString`实例,然后调用`initWithCString:encoding:`来设置该实例的值为传入的C字符串。最后通过`AUTORELEASE`确保在适当的时机释放这个...

    ios中文开发教程资料全收录

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

    iphone面试题

    obj = [self allocWithZone:NSDefaultMallocZone()]; obj = [obj initWIthCString:nullTerminatedCString encoding:encoding]; return [obj autorelease]; } ``` 解析: - `allocWithZone:`方法用于内存分配。 -...

    iOS UI开发指南

    - 重写 `allocWithZone:` 方法以确保始终使用 `sharedStore()` 来获取单例。 - 在初始化时添加五个随机条目到 `BNRItemStore` 中。 - **示例代码**: ```swift class BNRItemStore: NSObject { static let ...

    探索NSObject的协议精髓:Objective-C中的基础方法解析

    MyObject *copy = [[self class] allocWithZone:zone]; copy.name = [self.name copyWithZone:zone]; return copy; } - (id)mutableCopyWithZone:(NSZone *)zone { return [self copyWithZone:zone]; } - ...

    AVAudioPlayer.zip

    - 使用`+ (instancetype)allocWithZone:(nullable NSZone *)zone`创建AVAudioPlayer实例。 - 调用`- initWithContentsOfURL:error:`或`- initWithData:error:`方法加载音频文件。前者需要提供音频文件的URL,后者...

    Objective C 单例模式设计源码

    obj = [[NSObject allocWithZone]init]; NSObject类参考文档里记录第三种方法是因为历史原因遗留下来的,在当前的Objective C中已经不再使用,所以我们就不考虑这种方式了。下面让我们主要看一下前两种方式。 第...

    Objective-C基础教程.docx

    - 特殊情况下使用`+ (instancetype)allocWithZone:(NSZone *)zone`等初始化方法。 4. **继承**: - 使用`&lt;父类名&gt;`在`@interface`声明时指定继承关系。 - 子类可以通过`super`关键字调用父类的方法或属性。 5. ...

    iOS设计模式之原型设计模式

    MyPrototype *copy = [[MyPrototype allocWithZone:zone] init]; copy.name = [self.name copyWithZone:zone]; // 复制其他属性... return copy; } @end ``` 在Swift中,虽然没有直接的NSCopying协议,但是我们...

    复制对象(二)<NSCopying>协议和属性的copy特性对应的Demo

    Chocolate *copy = [[Chocolate allocWithZone:zone] init]; copy.brand = [self.brand copy]; copy.flavor = [self.flavor copy]; return copy; } @end ``` 在上述代码中,`Chocolate`类遵循了`NSCopying`协议...

    IOS单例模式调试代码

    +(instancetype)allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; } @end ``` 在Swift中,单例的实现相对简洁: ```swift class Singleton { static let sharedInstance = Singleton() ...

Global site tag (gtag.js) - Google Analytics