Archive for 六月 2009

Interface Builder参与的内存管理问题

I 要点:

  • 如果一个变量在类中被定义为了 IBOutlet 那么你无需对其进行实例化,xib载入器会对其初始化。
  • 如果一个变量在类中被定义为了 IBOutlet 那么你必须负责将其释放。xib载入器不会帮忙的… …

*切不要初始化两回,内存会溢出,而且对象锁定也会出错。

可以在视图控件(View)上连接数据控件(Model)么?

M 答案是”可以的”,MVC要点:

  • 数据控件(Models)不能有对View和Controller的访问
  • Controller(控制)需要有读写权限到View和Model上,因为其实Controller的作用就是更新View视图的状态和Model的数据
  • View视图可以有读写权限到Controller,不过只能有只读权限到Models

mvc

关于索引计数(Reference Counting)的问题

M *retain值 = 索引计数(Reference Counting)

  • NSArray对象会retain(retain值加一)任何数组中的对象。当NSArray被卸载(dealloc)的时候,所有数组中的对象会被执行一次释放(retain值减一)。不仅仅是NSArray,任何收集类(Collection Classes)都执行类似操作。例如NSDictionary,甚至UINavigationController。
  • Alloc/init建立的对象,索引计数为1。无需将其再次retain。
  • [NSArray array]和[NSDate date]等“方法”建立一个索引计数为1的对象,但是也是一个自动释放对象。所以是本地临时对象,那么无所谓了。如果是打算在全Class中使用的变量(iVar),则必须retain它。
  • 缺省的类方法返回值都被执行了“自动释放”方法。(*如上中的NSArray)
  • 在类中的卸载方法“dealloc”中,release所有未被平衡的NS对象。(*所有未被autorelease,而retain值为1的)

多线程之NSInvocationOperation

T 多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。

本次介绍NSOperation的子集,简易方法的NSInvocationOperation:

@implementation MyCustomClass
 
- (void)launchTaskWithData:(id)data
{
    //创建一个NSInvocationOperation对象,并初始化到方法
    //在这里,selector参数后的值是你想在另外一个线程中运行的方法(函数,Method)
    //在这里,object后的值是想传递给前面方法的数据
    NSInvocationOperation* theOp = [[NSInvocationOperation alloc] initWithTarget:self
                    selector:@selector(myTaskMethod:) object:data];
 
    // 下面将我们建立的操作“Operation”加入到本地程序的共享队列中(加入后方法就会立刻被执行)
    // 更多的时候是由我们自己建立“操作”队列
    [[MyAppDelegate sharedOperationQueue] addOperation:theOp];
}
 
// 这个是真正运行在另外一个线程的“方法”
- (void)myTaskMethod:(id)data
{
    // Perform the task.
}
 
@end

一个NSOperationQueue 操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的的线程数量等等。下面是建立并初始化一个操作队列:

@interface MyViewController : UIViewController {
 
    NSOperationQueue *operationQueue;
    //在头文件中声明该队列
}
@end
 
@implementation MyViewController
 
- (id)init
{
    self = [super init];
    if (self) {
        operationQueue = [[NSOperationQueue alloc] init]; //初始化操作队列
        [operationQueue setMaxConcurrentOperationCount:1];
        //在这里限定了该队列只同时运行一个线程
        //这个队列已经可以使用了
    }
    return self;
}
 
- (void)dealloc
{
    [operationQueue release];
    //正如Alan经常说的,我们是程序的好公民,需要释放内存!
    [super dealloc];
}
 
@end

简单介绍之后,其实可以发现这种方法是非常简单的。很多的时候我们使用多线程仅仅是为了防止主线程堵塞,而NSInvocationOperation就是最简单的多线程编程,在iPhone编程中是经常被用到的。

属性项目的定义(property)

@property (copy, nonatomic) NSString *title;

什么是assign,copy,retain之间的区别?

  • assign: 简单赋值,不更改索引计数(Reference Counting)。
  • copy: 建立一个索引计数为1的对象,然后释放旧对象
  • retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1

retain的实际语法为:

- (void)setName:(NSString *)newName {
    if (name != newName) {
       [name release];
       name = [newName retain];
       // name’s retain count has been bumped up by 1
    }
}

说了那么麻烦,其实接下来的话最重要:

?如果你不懂怎么使用他们,那么就这样 ->

  • 使用assign: 对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char, 等等)
  • 使用copy: 对NSString
  • 使用retain: 对其他NSObject和其子类

nonatomic关键字:

atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。

NSString的内存管理

CS193P的笔记上有如下实例:

aString = @"I am a string that 2 years old, man!";

这种情况下,字符串储存和管理由系统做,我们不用操心。

aString = [NSString stringWithFormat:@"I am a string that %d years old, man!",2];

第二种情况下,我们需要去retain和release这个字符串,系统不管。

Objective-C内存管理

1,你初始化(alloc/init)的对象,你需要释放(release)它。例如:

NSMutableArray aArray = [[NSArray alloc] init];

后,需要

[aArray release];

2,你retain或copy的,你需要释放它。例如:

[aArray retain]

后,需要

[aArray release];

3,被传递(assign)的对象,你需要斟酌的retain和release。例如:

obj2 = [[obj1 someMethod] autorelease];

对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSInteger,NSString)时: 你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。

为什么不能直接调用dealloc而是release

M 在看过CS193P的笔记后,以下是真正有用的句子:

dealloc不等于C中的free,dealloc并不将内存释放,也不会将索引计数(Reference counting)降低。于是直接调用dealloc反而无法释放内存。

在Objective-C中,索引计数是起决定性作用的。

对比:id与NSObject

N 以下是来自CS193P的笔记:

1,id关键字在编译时不被检查,而NSObject在编译时会被检查是否被调用一些错误方法。

2,id可以是任何对象,包括非NSObject对象

3,定义id的时候不使用*,NSObject却需要。

我的理解,基本上来讲,定义id类似于定义了一个地址(只有指针,抽象指针),而NSObject是确实的定义了一个逻辑对象的地址。

一个来自CS193P笔记的链接:

http://unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html

在子类(sub-class)中覆盖“初始化”和“默认”函数

I 在CS193P中,Evan强调了以下问题:

为什么要在覆盖init的时候需要检查是否在父类中作初始化,例如:

- (id)init
{
	if(self = [super init])
	{
	}
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}

主要是将本地对象用父类的init进行初始化,其中的等号“=”另一方面检查了是不是成功进行了初始化。CS193P提供了更多关于这个的信息:

http://cocoadev.com/index.pl?FactoryMethod

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

不仅仅是init等初始化对象,还有其他一些方法(Method)是需要对自己的父类打声招呼得,例如:

- (void)viewDidLoad {
    [super viewDidLoad];
 
}

那这个没有super这句会怎么样呢?没事,一样用。这个是一个delegate,所以需要收听的类应该会收听到的。但是既然缺省的方法就是这么写的,我们也没有必要特意去删除掉这个super方法。