OC的那些事

2017-05-05  本文已影响135人  nenhall
  1. NSObject * __weak someObject = [[NSObject alloc] init];, 这个someObject没有对象强引用他, 所以这行代码之后会立马被置为nil, NSObject * __weak someObject = self.someObject, 这个someObject在这行代码之后不会立刻被置为nil, 而是会在所在的代码块结束后被置为nil。

  2. oc中的对象是动态管理(内存)的, 是分配在heap(堆)上所以需要一个指针来指向它(才能访问), 所以对象类型需要用 星号 NSString * str;

  3. 比较是否相同: 使用 if(a==b) {}, 如果a,b是对象类型, 那么比较的是指针是否相同, 而不是比较值是否相同, 如果a, b是基本类型(int, double...), 那么比较的是值是否相同; 使用if ([a isEqual: b]) { }, 则比较的是a,b的值是否相同。

4. 初始化基本类型的时候尽量设置初始值, 因为编译器分配的初始值并不确定, 但是对象类型会默认初始化为nil。

oc中属性的getter和setter@property (nonatomic) NSString *name;

例如当有这样一个name属性的时候, 默认是readWrite的, 编译器会自动生成一个set (setName:)和get(-(NSString *)name)方法, 这个时候可以通过set或者get方法访问到name, 如果申明为(readonly), 那么将只会生成get方法

[self setName:@"set name"];

NSString *getName = [self name];

也可以通过点语法访问(实际上是会自动调用set和get方法)

self.name = @"set name";

NSString *dotName = self.name;

同时你可以重写name的get(懒加载...)和setter(拦截set方法)...对应name属性, 编译器会生成(synthesize)一个 _name 允许我们直接通过指针访问变量, 而不会调用get方法, 所以通过_xx访问的变量不会调用懒加载(get方法), 所以在写懒加载方法的时候, 不能使用self.xx(造成死循环), 而要使用_xx -

(NSString *)name {
// 这里面不能使用self.name , 因为点语法会调用这个get方法, 造成死循环
if (_name == nil) {
_name = @"name";
}
return _name;
}

同时这个synthesize的名字我们是可以自己修改的, 使用如下的语法@synthesize name = customName;

那么这个时候就不能通过 __name访问到name了, 因为我们已经指定了通过customName才能访问到了NSString *getName = customName;

当然如果, 你是这样写的 @synthesize name;, 并没有指定名字, 这个时候访问的时候就直接使用变量名而不需要加下划线( _ )了 name = @"set name"; ??这个时候就比较爽了, 和swift,java这些一样, 不需要self,this了;

5,分类(category)定义的函数和属性在运行时中和原生的class中定义的东西并没有区别At runtime, there’s no difference between a method added by a category and one that is implemented by the original class

6,不过分类中定义的属性, 编译器并不会自动生成getter和setter, 以及_XX变量来访问,需要自己提供getter和setter, 并且需要使用运行时才能绑定这个属性到这个类中, 实现原生类中定义的属性的效果

///例如可能是这样的使用

static const void *propertyKey = &propertyKey;

/// 将value通过运行时绑定到self

objc_setAssociatedObject(self, propertyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

/// 将value在运行时中通过propertyKey取出绑定的值

id value = objc_getAssociatedObject(self, propertyKey);

7,同时分类也可以用来将一个复杂的类中的代码分块(swift的extension可以有相似的作用), 使得代码组织更好, 例如可以将tableView的delegate, 和Datasource在分类中实现。

@implementation ViewController(tableview)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

}

@end

8,但是在使用category来扩展Cocoa的原生类的时候, 要注意函数的命名如果是和原生已有的函数名相同,那么将会发生不可预料的结果(不能确定哪一个方法在运行时会被调用), 因此建议在自己的函数名前面加上前缀, 就像重写 +load() 来实现各种黑魔法的时候也是可能会发生不可预料的结果, 因为同一个项目中可能有多个地方重写了这个类的 +load方法

9,初始化NSArray的时候, 如果通过NSArray *arr1 = @[object1, object2];, 不需要以nil结尾, 如果通过构造方法初始化, 则需要传入nil结尾, 同时, 如果中间的对象有nil, 那么将在中间nil就结束了, NSArray *arr2 = [NSArray arrayWithObjects:object1, object2, object3, nil, object4, object5, nil] 这个arr2只可能会存储第一个nil前的对象

10,如果在数组中一定要存储nil, 那么只能用NSNull来代替

11,如果NSArray中存储的是NSArray, NSDictionary, NSString, NSData, NSDate , NSNumber这些类型的对象, 那就可以直接写入disk并且读取disk的数据做持久化数据操作[array writeToURL:fileURL atomically:YES], 但是如果是有其他的类型, 就需要使用归档来实现了

12,在oc中block是object类型的, 所以是可以存储在NSArray...中, 同时在调用block的时候, 如果block为nil(未赋值), 那么程序将crash.

13,oc中block可以捕获变量, 什么意思呢 --- 就是block会默认捕获到变量的值, 在之后不受到原来变量的改变的影响, 例如

int anInteger = 42;

void (^testBlock)(void) = ^{

NSLog(@"Integer is: %i", anInteger);

};

anInteger = 84;

testBlock(); ---- 输出的值仍然为 42

14,第二种block捕获变量的方式, 是捕获变量的指针, 被捕获的变量值改变, 则block中的变量值也改变了,不过需要对变量进行__block标记, 例如上面的代码, 只改变一点, 结果就变了

__block int anInteger = 42;

void (^testBlock)(void) = ^{

NSLog(@"Integer is: %i", anInteger);

};

anInteger = 84;

testBlock(); --- 输出值这时是 84

15,preferredMaxLayoutWidth用来制定最大的宽,一般用在多行的UILabel中

systemLayoutSizeFittingSize方法能够获得view的高度

iOS7有两个很有用的属性,topLayoutGuide和bottomLayoutGuide,这个两个主要是方便获取UINavigationController和UITabBarController的头部视图区域和底部视图区域。

16,AutoLayout关于更新的几个方法的区别:

setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。

layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。

layoutSubviews:系统重写布局

setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始

updateConstraintsIfNeeded:告知立刻更新约束

updateConstraints:系统更新约束.

17,AutoLayout情况如何计算UITableView的变高高度

主要是UILabel的高度会有变化,所以这里主要是说说label变化时如何处理,设置UILabel的时候注意要设置preferredMaxLayoutWidth这个宽度,还有ContentHuggingPriority为UILayoutPriorityRequried

CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 10 * 2;
textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.preferredMaxLayoutWidth = maxWidth;
[self.contentView addSubview:textLabel];
[textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
   make.top.equalTo(statusView.mas_bottom).with.offset(10);
   make.left.equalTo(self.contentView).with.offset(10);
   make.right.equalTo(self.contentView).with.offset(-10);
   make.bottom.equalTo(self.contentView).with.offset(-10);
}];
[_contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
如果版本支持最低版本为iOS 8以上的话可以直接利用UITableViewAutomaticDimension在tableview的heightForRowAtIndexPath直接返回即可。
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 80; //减少第一次计算量,iOS7后支持

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
   // 只用返回这个!
   return UITableViewAutomaticDimension;
}

但如果需要兼容iOS 8之前版本的话,就要回到老路子上了,主要是用systemLayoutSizeFittingSize来取高。步骤是先在数据model中添加一个height的属性用来缓存高,然后在table view的heightForRowAtIndexPath代理里static一个只初始化一次的Cell实例,然后根据model内容填充数据,最后根据cell的contentView的systemLayoutSizeFittingSize的方法获取到cell的高。具体代码如下

//在model中添加属性缓存高度
@interface DataModel : NSObject
@property (copy, nonatomic) NSString *text;
@property (assign, nonatomic) CGFloat cellHeight; //缓存高度
@end

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
   static CustomCell *cell;
   //只初始化一次cell
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])];
   });

   DataModel *model = self.dataArray[(NSUInteger) indexPath.row];
   [cell makeupData:model];
   if (model.cellHeight <= 0) {
       //使用systemLayoutSizeFittingSize获取高度
       model.cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
   }
   return model.cellHeight;
}

18,UILayoutFittingCompressedSize 表示返回最小可能的值

UILayoutFittingExpandedSize 表示返回最大可能的值

19,使用masonry进行label的多行显示设置时,主要是如下两个参数的设置

1、@property(nonatomic)CGFloat preferredMaxLayoutWidth

2、- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis

//如下

UILabel *label3 = [[UILabelalloc] initWithFrame:CGRectZero];

[self.viewaddSubview:label3];

label3.backgroundColor = [UIColorcolorWithWhite:0.5alpha:0.3];

label3.text =@"Masonry是一个轻量级的布局框架与更好的包装AutoLayout语法。Masonry有它自己的布局方式,描述NSLayoutConstraints使布局代码更简洁易读。Masonry支持iOS和Mac OS X。Masonry github地址:https://github.com/SnapKit/Masonry";

label3.preferredMaxLayoutWidth = (WidthScreen -10.0 * 2);

[label3 setContentHuggingPriority:UILayoutPriorityRequiredforAxis:UILayoutConstraintAxisVertical];
label3.numberOfLines =0;
[label3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(10.0);
make.right.mas_equalTo(-10.0);
make.top.mas_equalTo(10.0);
}];


分类
Category的作用
可以在不修改原来类的基础上, 为这个类扩充一些方法

一个庞大的类可以分模块开发

一个庞大的类可以由多个人来编写,更有利于团队合作

Category的使用注意:



分类只能增加方法,不能增加成员变量

在分类方法的实现中可以访问原来类的成员变量

分类中可以重新实现原来类的方法,但是会覆盖原来的方法

方法的调用优先级:分类->原来的类->父类

类的启动过程



+load方法



在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法(只会调用一次)

先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load

先加载元原始类,再加载分类

不管程序运行过程有没有用到这个类,都会调用+load加载

+initialize



在第一次使用某个类时(比如创建对象等),只会调用一次+initialize方法

一个类只会调用一次+initialize方法,先调用父类的,再调用子类的


打开文件共享权限:在Info.plist文件中添加Application supports iTunes file sharing ,并设置为YES,即可通过iTunes直接导出来



手机上自动安装应用:

`itms-services://?action=download-manifest&url=https://www.xxxxx.com/abc......`
上一篇下一篇

猜你喜欢

热点阅读