Objective-C中的@Selector

其作用相当于函数指针,现在我看到的大多说用法都是在调用某些函数需要传递一个 函数指针 参数时,使用@selector。它会在当前类里面查找selector后面所跟的函数,返回一个SEL类型的值。

SEL变量的执行.用performSelecor方法来执行.

[对象 performSelector:SEL变量 withObject:参数1 withObject:参数2];

在调用respondsToSelector:@selector(method)时,这个method只有在该方法存在参数时需要 “:”,如果该方法不需要参数就不需要加这个冒号。否则,编译不会报错,只是执行返回的值不对。当然如果方法有多个参数,需要多个冒号,参数有名称的需要带上参数名称。

如:有如下方法:

-(NSString*)toXmlString;

此时调用类似于:

[self respondsToSelector:@selector(toXmlString)]

如果toXmlString方法的定义为:

-(NSString*)toXmlString:(NSString*)prefix;

那么调用就必须加上冒号,如:[self respondsToSelector:@selector(toXmlString:)]

  • -(BOOL) isKindOfClass: classObj 用来判断是否是某个类或其子类的实例
  • -(BOOL) isMemberOfClass: classObj 用来判断是否是某个类的实例
  • -(BOOL) respondsToSelector: selector 用来判断是否有以某个名字命名的方法(被封装在一个selector的对象里传递)
  • +(BOOL) instancesRespondToSelector: selector 用来判断实例是否有以某个名字命名的方法. 和上面一个不同之处在于, 前面这个方法可以用在实例和类上,而此方法只能用在类上.
  • -(id) performSelector: selector 执行某个方法

UINavigationController的popToViewController用法

UINavigationController popToViewController可以指定UINavigationController跳转到某一个指定UIViewController控制器

[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:2] animated:YES];

for (UIViewController *controller inself.navigationController.viewControllers) {
if ([controller isKindOfClass:[你要跳转到的Controller class]]) {
[self.navigationControllerpopToViewController:controller animated:YES];
}
}

Objective-C的self.用法的一些总结

关于什么时间用self. , 其实是和Obj-c的存取方法有关, 不过网上很多人也都这么解答的, 那它为什么和存取方法有关? 怎么有关的? 并没有多少人回答出来. 同时关于内存管理的内容, 请大家看旺财勇士的Objective-C内存管理总结~CC专版 , 有些东西我就不多解释了.

进入正题, 我们经常会在官方文档里看到这样的代码:

MyClass.h

@interface MyClass : NSObject {
MyObject *myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end

MyClass.m
@synthesize myObject;

-(id)init{
if(self = [super init]){
MyObject * aMyObject = [[MyObject alloc] init];
self.myObject = aMyObject;
[aMyObject release];
}
return self;
}
有人就问, 为什么要这么复杂的赋值? 为什么要加self. ? 直接写成self.myObject = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的?

现在我们来看看内存管理的内容:

先看间接赋值的:

1.加self.

MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
self.myObject = aMyObject; //myObject retainCount = 2;
[aMyObject release];//myObject retainCount = 1;

2. 不加self.

MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
myObject = aMyObject; //myObject retainCount = 1;
[aMyObject release];//对象己经被释放

再看直接赋值的:

3.加self.

self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2;

4. 不加self.

myObject = [[MyObject alloc] init]; //myObject retainCount = 1;

现在是不是有点晕, 我们先来把代码改一下, 官方的一种常见写法:

MyClass.h

@interface MyClass : NSObject {
MyObject * _myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end

MyClass.m

@synthesize myObject = _myObject;

OK, 你现在再试下, 如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误, 为什么呢, 这里就是和Obj-c的存取方法有关了. 说白了很简单 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是为一个属性设置存取方法, 只是平时我们用的方法名和属性名是一样的,现在你把它写成不同的名字, 就会很清楚了. _myObject是属性本身, myObject是存取方法名.

现在我们知道self.是访问属性的存取方法了, 那存取方法又怎么工作的? self.myObject = [[MyObject alloc] init]; 为什么会有内存泄露?
关于nonatomic我不多解释了, 它不是我要讲的重点, 而且我也没完全搞清楚, 不误导大家. 我只说assign, retain ,copy.

get方法是:

-(MyObject*)myObject{
return _myObject;
}

Set方法是:

// assign
-(void)setMyObject:(id)newValue{
_myObject = newValue;
}
// retain
-(void)setMyObject:(id)newValue{
if (_myObject != newValue) {
[_myObject release];
_myObject = [newValue retain];
}
}
// copy
-(void)setMyObject:(id)newValue{
if (_myObject != newValue) {
[_myObject release];
_myObject = [newValue copy];
}
}

其实这些方法里还有别的内容, 并不只是这些. 而且这些方法可以被重写. 比如你写一个

-(MyObject*)myObject{
return _myObject;
}

放在你的类里, 你调用self.myObject时(不要把它放在等号左边, 那会调用get方法)就会调用这个方法.

这里多说一句, @property 是为你设置存取方法, 和你的属性无关, 你可以只写一句

@property (readonly) NSString *name;

在你的类里实现

-(NSString*)name{
NSLog(@”name”);
return @”MyClass”;
}

同样可以用self.name调用.

现在回头说说我们开始的那四个赋值, 当不用self.的时候,  那句话只是一般的赋值, 把一个指针赋给另一个指针, 不会对分配的内存有任何影响, 所以2中不要最后[aMyObject release];这句话和4是一回事. 这里就不多说了.我们看看1和3,
当调用setMyObject:方法时, 对newValue 做了一次retain操作, 我们必须把原来的newValue释放掉, 不然就会内存泄露, 在1里, 我们有个aMyObject可以用来释放, 在3里, 我们无法释放它, 所以, 在3里, 我们会多出来一个retainCount. 内存泄露了.

说了这么多, 我只想让大家清楚, 什么是调用属性本身, 什么是调用存取方法. 怎么样才能避免内存泄露, 而且, 以上例子里是在自己类里的调用, 如果这个类被别的类调用时, 更要注意一些,

顺便说一下, 如果你想在其它类访问对象属性, 而不是通过存取方法, 你可以用myClass -> myObject来访问, 这样是直接访问对象本身, 不过你先要把myObject设成@public. 但这个是官方不提倡的,

代码比较简单, 我还是发出来, 高人们可以忽略了.

 

NSString与NSInteger的互换

NSInteger转化NSString类型:

[NSString stringWithFormat: @”%d”, NSInteger];

 

NSString转化 NSInteger类型:

NSInteger = [NSString intValue];

 

objective-c 入门学习建议

1、把基础知识学习好。c、数据结构、面向对象。

2、一定要不断在调试程序,写程序,不能光看。

3、不要一开始就学习cocoa框架,至少要把objective-c搞明白后,再学习。

4、不停的到技术社区学习,提问。

5、切记不要一口吃个胖子,所有技术都是一步一步学习到的。

6、耐心, 坚持编程训练!

 

iPhone内存管理的一些整理

内存管理个人总结

无论编写任何程序,都需要确保能够有效和高效地管理资源。程序内存就是这些资源中的一种。在Objective-C程序中,必须确保所创建的对象,在不再需要它们的时候被销毁。

注意:iOS不提供垃圾回收机制。

一般规则:

只能释放或自动释放自己所拥有的对象

一般以alloc ,new, copy创建的对象都具有所有权,或者如果向一个对象发送了一条retain消息,也会获得该对象的所有权。

此时需要用release进行释放或者调用自动释放池autorelease;自动释放池的调用是系统来完成的。

需要将接收到的对象存储为某个实例变量的属性,您必须保留或复制该对象。

释放实例:

UIView *view 1= [[UIView alloc]init];

Self.view = view1;

[view1 release];

不需要释放的例子:

UIButton*button=[UIButton buttonWithType:UIButtonTypeRoundedRect];

由于调用的类方法没有使用alloc,new,copy等所以它自动释放,不需要手动释放。

调用自动释放池的例子:

-(NSArray *)rearray

{

NSArray *array = [[NSArray alloc]initWithObjects:@”1”,@”2”,nil];

return [array autorelease];

}

这个方法既没有破坏内存规则,又避免了接收值的使用者不知道何时销毁对象的现象。如果直接return array会造成内存泄露,如果先释放在返回的话会返回无效对象([array release];return array;)这也是错误的。当然如果直接调用类方法就可以这样做:

-(NSArray *)rearray

{

NSArray *array = [NSArray arrayWithObjects:@”1”,@”2”,nil];

return array;

}

 

在 Objective-C 程序中,对象会被创建和销毁。为了确保应用程序不会使用不必要的内存,对象应该在不需要它们的时候被销毁。当然,在需要对象时保证它们不被销毁也很重要。

任何对象都可能拥有一个或多个所有者。只要一个对象至少还拥有一个所有者,它就会继续存在。如果一个对象没有所有者,则运行时系统会自动销毁它。

可以使用retain来获得一个对象的所有权。

保留计数(retainCount)

在调用retain方法后通过引用计数—通常被称为“保留计数”—实现的。每个对象都有一个保留计数。

创建一个对象时,该对象的保留计数为1。

向一个对象发送retain消息时,该对象的保留计数加1。

向一个对象发送release消息时,该对象的保留计数减1。

向一个对象发送autorelease消息时,该对象的保留计数会在将来的某个阶段减1。

如果一个对象的保留计数被减为0,该对象就会被回收.(直接调用dealloc进行释放)。

当显式地查询对象的保留计数是多少。由于添加了保护机制,当保留计数被减为0时,控制台输出仍然为1因此会造成误导。

共享对象的有效性

Cocoa的所有权策略规定,被接收的对象通常应该在整个调用方法的作用域内保持有效。此外,还可以返回从当前作用域接收到的对象,而不必担心它被释放。对象的getter方法返回一个缓存的实例变量或者一个计算值,这对您的应用程序来说无关紧要。重要的是,对象会在您需要它的这段期间保持有效。

这一规则偶尔也有一些例外情况,主要可以总结为以下两类。

当对象从一个基本的集合类中被删除的时候。

heisenObject = [array objectAtIndex:n];

[array removeObjectAtIndex:n];

// heisenObject could now be invalid.

当对象从一个基本的集合类中被删除时,它会收到一条release(不是autorelease)消息。如果该集合是这个被删除对象的唯一所有者,则被删除的对象(例子中的heisenObject)将被立即回收。

当一个“父对象”被回收的时候。

id parent = <#create a parent object#>;

// …

heisenObject = [parent child] ;

[parent release]; // Or, for example: self.parent = nil;

// heisenObject could now be invalid.

在某些情况下,您通过另外一个对象得到某个对象,然后直接或间接地释放父对象。如果释放父对象会使其被回收,而且父对象是子对象的唯一所有者,那么子对象(例子中的heisenObject)将同时被回收(假设它在父对象的dealloc方法中收到一条release而非autorelease消息)。

为了防止这些情况发生,您要在接收heisenObject后保留该对象,并

在使用完该对象后对其进行释放,例如:

 

heisenObject = [[array objectAtIndex:n] retain];

[array removeObjectAtIndex:n];

// use heisenObject.

[heisenObject release];

如果在您的类中有实例变量对象,您必须实现一个dealloc方法来释放它们,然后调用超类的dealloc实现。

重要:决不要直接调用另一个对象的dealloc方法。

保留循环

在某些情况下,两个对象之间可能会出现循环引用的情况,也就是说,每一个对象都包含一个实例变量引用对方对象。例如,考虑一个文本程序,程序中对象间的关系如图1所示。“文档(Document)”对象为文档中的每个页面创建一个“页(Page)”对象。每个Page对象具有一个实例变量,用来跟踪该页所在的文档。如果Document对象保留了Page对象, 同时Page对象也保留Document对象,则这两个对象都永远不会被释放。只有Page对象被释放,Document的引用计数才能变为0,而只有Document对象被回收,Page对象才能被释放。

 

针对保留循环问题的解决方案是“父”对象应保留其“子”对象,但子对象不应该保留其父对象。因此,在图1中,document对象要保留page对象,但page对象不保留document对象。子对象对其父对象的引用是一个弱引用的例子,这部分内容在“对象的弱引用”有更充分的描述。

保留一个对象创建了一个对该对象的“强”引用。一个对象只有在它的所有强引用都被释放后才能被回收。因此,一个对象的生命周期取决于其强引用的所有者。在某些情况下,这种行为可能并不理想。您可能想要引用一个对象而不妨碍对象本身的回收。对于这种情况,您可以获取一个“弱”引用。弱引用是通过存储一个指向对象的指针创建的,而不是保留对象。

NSMutableArray *array;

NSUInteger i;

// …

for (i = 0; i < 10; i++) {

NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i];

[array addObject:allocedNumber];

[allocedNumber release];

}

 

在这段代码中,您需要在for循环的作用域内向allocedNumber发送release消息,以抵消之前的alloc。由于数组在用addObject:方法添加数字时对其进行了保留,因此只要它还在数组中就不会被释放。

要理解这一点,您要把自己放在实现这种集合类的作者的位置。您要确保交给您管理的对象不能在您的眼皮底下消失,所以您要在这些对象被加入集合中时向它们发送retain消息。如果它们被删除,您还必须相应地发送release消息,并且在您自己的dealloc方法中,您还应该向其余的对象发送release消息。

您可以使用常见的alloc和init消息来创建一个NSAutoreleasePool对象,并使用drain(如果您向一个自动释放池发送autorelease或retain消息,会引发异常—要了解release和drain之间的差异,请参考“垃圾回收”)销毁它。自动释放池应该总是在与它被创建时所处的相同上下文环境(方法或函数的调用,或循环体)中被销毁。

 

自动释放池被置于一个堆栈中,虽然它们通常被称为被“嵌套”的。当您创建一个新的自动释放池时,它被添加到堆栈的顶部。当自动释放池被回收时,它们从堆栈中被删除。当一个对象收到送autorelease消息时,它被添加到当前线程的目前处于栈顶的自动释放池中。

 

嵌套自动释放池的能力是指,您可以将它们包含进任何函数或方法中。例如,main函数可以创建一个自动释放池,并调用另一个创建了另外一个自动释放池的函数。或者,一个方法可以有一个自动释放池用于外循环,而有另一个自动释放池用于内循环。嵌套自动释放池的能力是一种很显著的优势,但是,当发生异常时也会有副作用

我们通常会提及自动释放池是被嵌套的,如清单1所示。但是,您也可以认为嵌套自动释放池位于一个堆栈中,其中,“最内层”的自动释放池位于栈顶。如前所述,嵌套自动释放池实际上是这样实现的:程序中的每个线程都维护一个自动释放池的堆栈。当您创建一个自动释放池时,它被压入当前线程的堆栈的栈顶。当一个对象被自动释放时—也就是说,当一个对象收到一条autorelease消息或者当它作为一个参数被传入addObject:类方法时—它总是被放入堆栈顶部的自动释放池中。

在垃圾回收环境中,release是一个空操作。因此,NSAutoreleasePool提供了drain方法,在引用计数环境中,该方法的作用等同于调用release,但在垃圾回收环境中,它会触发垃圾回收(如果自上次垃圾回收以来分配的内存大于当前的阈值)。因此,在通常情况下,您应该使用drain而不是release来销毁自动释放池。

 

Objective-c常用函数和常数

算术函数

函数名 说明
int rand() 随机数生成。
(例)
srand(time(nil)); //随机数初期化
int val = rand()%50; //0~49之间的随机数
int abs(int a) 整数的绝对值
(例)int val = abs(-8);
→8
※浮点数的时候用fabs。
double fabs(double a) 浮点数的绝对值
(例)double val = fabs(-12.345);
→12.345
※整数的时候用abs。
double floor(double a) 返回浮点数整数部分(舍弃小数点)
(例)double val = floor(12.345);
→12.000
double ceil(double a); 返回浮点数整数部分(舍弃小数点部分,往个位数进1)
(例)double val = ceil(12.345);
→13.000
double pow(double a, double b) a的b次方
(例)double val = pow(2, 3);
→8
double sqrt(double a) a的平方根
(例)double val = sqrt(2);
→1.41421356

三角函数

函数名 说明
double cos(double a) 余弦函数 (a:弧度)
double sin(double a) 正弦函数 (a:弧度)
double tan(double a) 正切函数 (a:弧度)
double asin(double a) 反正弦值 (a:弧度)
double acos(double a) 反余弦函数(a:弧度)
double atan(double a) 反正切函数
double atan2(double a, double b) 返回给定的 a 及 b 坐标值的反正切值

指数函数

函数名 说明
double log(double a) 以e 为底的对数值
double log10(double a) 对数函数log

常数

常数名 说明
M_PI 圆周率(=π)
M_PI_2 圆周率的1/2(=π/2)
M_PI_4 圆周率的1/4(=π/4)
M_1_PI =1/π
M_2_PI =2/π
M_E =e
M_LOG2E log_2(e)
M_LOG10E log_10(e)