关于什么时候用全局变量,什么时候用self.赋值的问题,其实是和Objective-c的存取方法有关, 网上很多人也都这么解答的,不过如何与存取方式有关究竟他们之间的是什么样的关系就很少有同学回答了。我总结了一下, 发出来给大家参考. 有什么问题请大家斧正.
进入正题, 我们经常会在官方文档里看到这样的代码:
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; }
创建一个Student类,继承NSObject类,代码:
#import <Foundation/Foundation.h> @interface Student : NSObject{ NSString *idd; NSString *name; } @property (nonatomic, retain) NSString *idd; @property (nonatomic, retain) NSString *name; @end
.m文件 代码:
#import "Student.h" @implementation Student @synthesize idd,name; - (void)dealloc { [idd release]; [name release]; [super dealloc]; } @end
使用@propety @synthesize实现Student的成员属性的set get方法。通常我们在其他类里访问Student的成员属性的做法:
获取student的名字通过student.name,给名字赋值[student setName:@“jordy”]; 其中student是Student类对象,如果在Student类内部访问其成员属性使用[self setName:@”jordy”], 访问使用self.name;
在Student.h和Student.m文件,是我们习惯性的写法,但似乎还是不能解释什么加self和不加self的区别,请看下面代码,是另一种习惯性的写法,还以Student类为例:
.h文件 代码:
#import <Foundation/Foundation.h> @interface Student : NSObject{ NSString *_idd; NSString *_name; } @property (nonatomic, retain) NSString *idd; @property (nonatomic, retain) NSString *name; @end
.m文件 代码:
#import "Student.h" @implementation Student @synthesize idd = _idd; @synthesize name = _name; - (void)dealloc { [_idd release]; _idd = nil; [_name release]; _name = nil; [super dealloc]; } @end
可以注意到上述代码,与之前的代码,在.h文件name变量改写为了_name;在.m文件中@sythesize的写法也发生了变化。
如果通过方法self._name获取属性的值,xcode编译器会提示错误,其实这也就说明了,我们通常使用self.name实际使用的是student类name的get方法,同理name的set方法亦是如此。
有人就问, 为什么要这么复杂的赋值? 为什么要加self. ? 直接写成self.myObject = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的?
接下来从内存管理来说明使用self.和不使用self的区别
现在我们来看看内存管理的内容:
ViewController.h文件,使用Student类,代码如下:
#import <UIKit/UIKit.h> @class Student; @interface ViewController : UIViewController{ Student *_student; } @property (nonatomic, retain) Student *student; @end
ViewController.m文件,代码:
#import "ViewController.h" #import "Student.h" @implementation ViewController @synthesize student = _student; - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } #pragma mark - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; } - (void) dealloc { [_student release]; _student = nil; [super dealloc]; }
其它的方法没有使用到,所以这里就不在显示了。
在ViewController.m的viewDidLoad方法中创建一个Student类的对象
Student *mystudent = [[Student alloc] init]; self.student = mystudent; [mystudent release];
这是相信有人会有疑问了,问什么创建student对象要这么复杂,似乎直接使用self.student = [[Student 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. 但这个是官方不提倡的,
代码比较简单, 我还是发出来, 高人们可以忽略了.
来源:http://mobile.51cto.com/iphone-313096.htm
objective-c中,当一个类使用到另一个类时,并且在类的头文件中需要创建被引用的指针时,
如下面代码:
A.h文件
#import "B.h" @interface A : NSObject { B *b; } @end
为了简单起见:A类是引用类,B类是被引用类,这里先不考虑A类的实现文件。
通常引用一个类有两种办法:
一种是通过#import方式引入;另一种是通过@class引入;
这两种的方式的区别在于:
1、#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息;
2、使用@class方式由于只需要只要被引用类(B类)的名称就可以了,而在实现类由于要用到被引用类中的实体变量和方法,所以需要使用#importl来包含被引用类的头文件;
3、通过上面2点也很容易知道在编译效率上,如果有上百个头文件都#import了同一 个文件,或者这些文件依次被#improt(A->B, B->C,C->D…),一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了;
4、对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类,B类的代码:
#import "A.h" @interface B : NSObject { A *a; } @end
当程序运行时,编译会报错,
当使用@class在两个类相互声明,就不会出现编译报错。
由上可知,@class是放在interface中的,只是在引用一个类,将这个被引用类作为一个类型,在实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。
如:
A.m文件 #import "A.h" #import "B.h" @implementation A ......
举个例子说明一下。
在ClassA.h中
#import ClassB.h 相当于#include整个.h头文件。如果有很多.m文件#import ClassA.h,那么编译的时候这些文件也会#import ClassB.h增加了没必要的#import,浪费编译时间。在大型软件中,减少.h文件中的include是非常重要的。
如果只是 ClassB 那就没有include ClassB.h。仅需要在需要用到ClassB的.m文件中 #import ClassB.h
那么什么时候可以用呢?
如果ClassA.h中仅需要声明一个ClassB的指针,那么就可以在ClassA.h中声明
@ClassB
...
ClassB *pointer;
所以,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。 在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要import在@class中声明的类进来.
来源:http://www.itivy.com/iphone/archive/2012/1/29/objective-c-import-class.html
XML文件的代码
java文件的代码
运行结果图:
由上,AutoCompleteTextView 默认是输入二个字符自动提示。如果想设置输入第一个字符就进行提示,添加代码如下:
运行结果图: