1.#import和#include的区别,@class代表什么?
import会代入头文件的所有信息,包括实体百年来那个和方法等,
include也是带入文件的所有信息,跟#import差异是在#import引用的文件只会被引用一次,不会递归包含的问题
@class代表的意思是,不需要导入文件内容,也不需要知道如何定义的,我只告诉编译器这个就是个类的名称,
2.谈谈Objective-C的内存管理方式和过程?
在App运行时创造了大量的对象,Objective-C中的对象时存储在堆中的,系统并不会自动释放堆中的内存(基本类型也就是值类型是由系统自己管理的,放在栈上),so,ObjC的内存管理是需要开发去手动维护的。
一、引用计数
在Xcode4.2以后的版本中都是引入了ARC机制,程序编译时xcode可以自动给你的代码添加内存释放代码,但是如果编写释放内存的代码就会报错,所以需要在xcode中主动关闭ARC,这样才能有助于ObjC的理解。
在ObjC中,每个对象内部都会有一个retainCount整数,叫"引用计数",当一个对象创建之后它的引用计数为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数会在动在原有的基础上加1,当调用这个对象的release方法之后它的引用计数会减1,如果一个对象的引用计数为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象,在手动管理内存的时候需要遵循一个原则谁创建、谁释放。
当用户调用方法的时候实际上是在向这个对象发送一条消息,且ObjC中允许向一个nil的对象发送消息,在释放完成以后最好直接给这个对象设置为nil,为了防止野指针的出现,当这个对象的引用计数为0的时候,系统会自动调用dealloc方法来销毁对象,可以在这里将所有的野指针设置为nil。
在手动使用的时候有可能会遇到野指针问题,比如下面例子:
************************************************************
/*** DogModel.h ***/
************************************************************
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DogModel : NSObject
@property(nonatomic,copy)NSString *dogName;
-(void)doing;
@end
NS_ASSUME_NONNULL_END
************************************************************
/*** DogModel.m ***/
************************************************************
#import "DogModel.h"
@implementation DogModel
-(void)doing{
NSLog(@"%@ 会汪汪汪!",self.dogName);
}
-(void)dealloc{
NSLog(@"DogModel = (%@)dealloc method.",self.dogName);
[super dealloc];
}
@end
************************************************************
/*** UserModel.h ***/
************************************************************
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class DogModel;
@interface UserModel : NSObject
@property(nonatomic,copy)NSString *UserName;
- (void)setDog:(DogModel *) dog;
-(DogModel*)Dog;
@end
NS_ASSUME_NONNULL_END
************************************************************
/*** UserModel.m ***/
************************************************************
#import "UserModel.h"
@interface UserModel ()
{
DogModel *_Dog;
}
@end
@implementation UserModel
- (void)setDog:(DogModel *)dog{
_Dog = dog;
}
-(DogModel*)Dog{
return _Dog;
}
-(void)dealloc{
NSLog(@"UserModel = (%@)dealloc method.",self.UserName);
[_Dog release];
_Dog = nil;
[super dealloc];
}
@end
************************************************************
/*** 调用代码 ***/
************************************************************
UserModel *user = [[UserModel alloc]init];
user.UserName = @"struggle";
DogModel *dog=[[DogModel alloc]init];
dog.dogName=@"小黑";
user.Dog=dog;
[dog release];
dog=nil;
[user.Dog doing];
[user release];
user = nil;
抛出异常
Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
经过调试发现是野指针的问题,主要是下面两段代码出的问题。
/*** 调用代码 ***/
DogModel *dog=[[DogModel alloc]init];
dog.dogName=@"小黑";
user.Dog=dog;
[dog release];
dog=nil;
/*** UserModel.m ***/
- (void)setDog:(DogModel *)dog{
_Dog = dog;
}
当创建一个新的DogModel的实例变量 dog直接赋值给了user.Dog其真正目的只是给了user.Dog一个指针,user.Dog跟dog对应的内存空间是一样的。并且retainCount的值时1,当[dog release]后,retainCount = 0,这个对象也就释放掉了,而user.Dog实际上已经是一个野指针了。
解决方案
/*** UserModel.m ***/
- (void)setDog:(DogModel *)dog{
if(_Dog != dog){
[_Dog release];
_Dog = [dog retain];
}
}
这个方法,跟property中的(retain)是一样的
二、属性参数
@property自动实现你的属性的getter、setter方法,并且提供一些参数供选择
参数 | 详解 |
---|---|
atomic | 对属性加锁,多线程下县城安全,默认值 |
nonatomic | 对属性不加锁,多线程不安全,但是速度快 |
readwrite | 生成getter、setter方法,可读可写,默认值 |
readonly | 只生成getter方法,只读 |
assign | 直接赋值,默认值 |
weak | 相当于assign,多了一点就是对象被干掉时,weak引用自动设置为nil |
retain | 先release原来的值,再retain新值 |
Strong | 跟retain一样,在ARC中,不需要手动释放内存 |
copy | 先release原来的值,在copy新值 |
assign,setter方法
-(void)setNamenum:(int)num{
_Namenum=num;
}
retain,setter方法
-(void)setNamenum:(calss*)num{
if(_Namenum != num)
[_Namenum release];
_Namenum = [num retain];
}
copy,setter方法
-(void)setNamenum:(calss*)num{
if(_Namenum != num)
[_Namenum release];
_Namenum = [num copy];
}
三、自动释放池
在ObjC中还有一种自动释放机制, 使用的时候首先用@autoreleasepool关键字声明一个代码块,在代码块中初始化你的对象,如果对象在初始化的时候加入了autorelease方法,那么在这个代码块执行完成以后,在块中只要调用过autorelease方法的对象都会自动调用release方法。这就是自动释放池,这样release方法是一起呗调用的,可能有点用处。
@autoreleasepool {
UserModel *user1 = [[[UserModel alloc]init]autorelease];
user1.UserName = @"用户1";
UserModel *user2 = [[[UserModel alloc]init]autorelease];
user2.UserName = @"用户2";
}
打印结果
2019-01-25 23:26:53.031860+0800 MRC_demo[3264:662385] UserModel = (用户2)dealloc method.
2019-01-25 23:26:53.031967+0800 MRC_demo[3264:662385] UserModel = (用户1)dealloc method.
从结果来看自动释放池是一个队列,先进后出,压栈的形式。
根据理解又实验了2次:
- 在autorelease之后user2加上retain,实验证明user2没有被释放,说明autorelease只是调用了一次release
- 在autorelease之后user2加上release,程序崩溃Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)野指针,将user2 = nil就会解决,ObjC可以向nil对象发送消息。
自动释放池的总结
- autorelease不会改变retaincount的值,只是放入自动释放池
- 自动释放池,就是在代码块完成以后,调用autorelease的对象自动调用release方法(只是一次),如果对象的retaincount>1那么就没法释放
- 自动释放池是统一销毁,那么在这个代码块中占用的内存会相对来说多一点,可以使用多个自动释放池
3. Objective-C有私有方法吗?私有变量呢?
- 在ObjC中按照正常的规则来将是有私有方法,跟私有变量的,只要我不在.h文件声明就可以,只是在表面上是这样的,
- 因为ObjC是一个动态语言,我们可以通过runtime来动态的获取这些property、方法、ivar,得到名称或者直接修改值都是可以的。
代码如下:
/********************************************************/
对象类代码
/********************************************************/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UserModel : NSObject
- (instancetype)initWithUserName:(NSString *)name;
@end
NS_ASSUME_NONNULL_END
@interface UserModel ()
{
NSString * userName;
}
@end
@implementation UserModel
- (instancetype)initWithUserName:(NSString *)name{
self = [super init];
if(self){
userName = name;
}
return self;
}
- (void)doing{
NSLog(@"这是我的私有方法");
}
@end
/********************************************************/
调用私有方法代码
当然这段代码是将所有的对象都得到然后都屌用一遍
/********************************************************/
int Count;
Method *methodList = class_copyMethodList([UserModel class], &Count);
for (unsigned int i = 0; i < Count; i++) {
Method method = methodList[i];
NSLog(@"method --> %@", NSStringFromSelector(method_getName(method)));
objc_msgSend(user, NSSelectorFromString(method_getName(method)));
}
/********************************************************/
调用私有变量代码
这里是将所有的变量都取出来然后都修改成"我去"
/********************************************************/
int varcount;
Ivar *varlist = class_copyIvarList([user class], &varcount);
for (unsigned int i = 0; i < varcount; i++) {
Ivar ivar = varlist[i];
object_setIvarWithStrongDefault(user, ivar, @"我去");
NSLog(@"%@",object_getIvar(user, ivar));
}
当然runtime还是可以做到很多事情并不仅仅是这些,不过从题目来看已经够充分的说明了ObjC是否存在私有变量跟私有方法了。答案是不存在。
问题待续