日期: 2021 年 6 月 8 日

iOS中[super class]为何会输出子类类型?

iOS中[super class]为何会输出子类类型?

比如有一个自定义类DetailViewController继承自类UIViewController。那么我们对这个自定义类分别使用[self class]和[super class]将会分别输出如下结果:
[self class] DetailViewController
[super class] DetailViewController

1. 问题?

按照面向对象的思维, [super class] 应该会输出父类类型也就是UIViewController,而从结果可以看到这两个输出都是一样的,那这是什么原因导致的呢?

我们知道实际上在iOS中,对方法的调用是通过发送消息来完成的。也就是说使用 [self class] 时,会使用obj_msgSend(id theReceiver, SEL selector, ...)函数向Receiver来发送消息。而使用 [super class] 时,会使用obj_msgsendSuper(...)函数向Receiver来发送消息。
  • 1

2. 分析

1). 然而obj_msgSend(…)和obj_msgSendSuper(…)中Receiver都是self,这里的self也就是DetailViewController

2). [self class]和[super class]都会找到 NSObject中class方法

3). 当[super class]找到NSObject中的class方法以后,仍然会使用obj_msgSend(receiver, @selector(class))函数,因为receiver不变,所以输出的结果仍旧是DetailViewController

[super class]找到NSObject中class方法以后,reciever不变实际上是因为super只是一个“编译器指示符”,它和self指向的是相同的receiv

【iOS9】iOS9新特性之__kindof

OS9新增的类型修饰关键字__kindof,一起来看看它的用法和作用吧!

**__kindof:**表示当前类或者子类

**__kindof书写格式:**放在类型前面,表示修饰这个类型,例如:

定义一个Person类,用不同的类方法获取Person的对象,同时定义一个它的子类SonPerson:

1、常见的instancetype,会自动识别当前对象的类

  1. + (instancetype)person;

2、__kindof Person *,表示可以使Person类或者它的子类。

  1. + (__kindof Person *)person;

3、而这种方法只能表示Person类

  1. + (Person *)person;

iOS __kindof

1.数组声明

@property (nonatomic, strong) NSArray <UIVIew *> * viewArr;
2.如果UIButton添加进去就会报警告

// 这样写就不会有警告了
@property (nonatomic, strong) NSArray <__kindof UIVIew *> * viewArr;
3.结论

__kindof就是包含这个类型的子类。

 

iOS 蓝牙报错”The request is not supported.”

今天蓝牙通信的时候,发现突然返回一个error: The request is not supported.究竟是什么原因呢?下面我们来揭开这个错误的神秘面纱吧。

1.报错原因

蓝牙设备端开发,说是需要缓存一个什么,他自己也说不清,问了几遍,我就不问了。。

2.处理方法

① 蓝牙设备修改了一些方法之后,就可以收到返回数据了,但是还有一些问题;

3.事后语

鹅鹅鹅,今天又出现这个问题了。。疯ing [2019年20月23日]

这个问题是,设备是Android版本的,Android的APP端可以正常使用,所以他也不那么着急解决,先留着吧。。

[完] iOS ➕方法-方法 中的self具体表示什么?

//+ 方法
+ (instancetype)cellWithTableView:(UITableView *)tableView {
static NSString *cellID = @”unlockCellIdentifier”;
id cell = [tableView dequeueReusableCellWithIdentifier:cellID];
DLog(@”%@>>>%@”, self, [self superclass]);
if (!cell) {
DLog(@”%@”,self);
cell = [[self alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
return cell;
}
打印结果:
2018-07-18 14:34:26.964118+0800 bluetoothLock[4786:185390] [文件名:/Users/****/Desktop/iOS/****/****/classes/unlockRecord/view/TGUnlockRecordCell.m]
[函数名:+[TGUnlockRecordCell cellWithTableView:]]
[行号:44]
TGUnlockRecordCell>>>UITableViewCell
//- 方法
– (instancetype)cellWithTableView:(UITableView *)tableView {
static NSString *cellID = @”unlockCellIdentifier”;
id cell = [tableView dequeueReusableCellWithIdentifier:cellID];
DLog(@”%@>>>%@”, self, [self superclass]);
if (!cell) {
DLog(@”%@”,self);
cell = [self initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
return cell;
}
打印结果:
2018-07-18 14:37:44.904729+0800 bluetoothLock[4851:188018] [文件名:/Users/****/Desktop/iOS/****/****/classes/unlockRecord/view/TGUnlockRecordCell.m]
[函数名:-[TGUnlockRecordCell cellWithTableView:]]
[行号:44]
<TGUnlockRecordCell: 0x7fb1560b5a00; baseClass = UITableViewCell; frame = (0 0; 320 44); layer = <CALayer: 0x60400042c

iOS[super class]和[self class]

*近小编所在公司招 iOS 开发职位,小编也出了几道面试题考察下候选人的 iOS 开发水平,其中有一道题如下:

@implementation Student : Person

– (instancetype)init
{
self = [super init];
if (self) {

id obj1 = [self class];
id obj2 = [super class];
NSLog(@”%@”,obj1);
NSLog(@”%@”,obj2);

}
return self;
}
@end

大部分候选人回答的 [self class ]输出 Student , [Super class]输出 Person, 只有少部分候选人回答都是输出 Student ,当然至于为什么输出结果都是 Student, 很少有能回答出来的.

接下来小编通过将Student.m 转换成 Cpp 文件,带大家一块去看看 [self class] 和 [super class] 背后究竟做了那些事情.

通过终端讲Student.m 转成Student.cpp 文件

2 找到 Student 的 init 方法 分析代码

static instancetype _I_Student_init(Student * self, SEL _cmd) {
self = ((Student *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass(“Student”))}, sel_registerName(“init”));
if (self) {

id obj1 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName(“class”));
id obj2 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass(“Student”))}, sel_registerName(“class”));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_0,obj1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_1,obj2);

}
return self;
}

由此可见 当我们调用[self class] 时候实际上编译器*终给我们转成
objc_msgSend(self,@selector(class)) , 消息的接收者是当前所在类的实例对象 , 这个时候就会去 self 所在类 Student 去查找 class 方法 ,
如果当前类 Student 没有 class 会向Student 父类 Person 类找 class 方法,
如果 Person 类也没有找到 class 方法,*终会找到*顶级父类 NSObject 的 class 方法,
*终找到 NSObject 的 class 方法 ,并调用了object_getClass(self) ,由于消息接收者是 self 当前类实例对象,
所以*终 [self class]输出 Student

那么为什么明明调用了 super 这个关键字 返回的[super class] 还是 Student 呢 ?

通过上边代码可知 , [super class] *终编译器转化成了 objc_msgSendSuper(struct objc_super *,SEL) ,其中

/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;

/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
#endif

objc_super 是一个结构体,内部有一个 receiver 实例,和一 个 Class super_class,指向了当前类的父类 Class ,通过这个父类可以直接的从父类里边开始查找方法,由于消息接收者还是当前类的实例对象 self, *终如果在父类中没有找到class 这个方法,会在 Person 类的父类 NSObject 中去查找 class 方法,由于 NSObject 的 class 方法,调用的是 object_getClass(self) ,所以*终消息接收者为 student 实例对象,所以返回的还是 Student .

当然我们知道了[ super class] *终编译后代码样子,也可以自己手动去调用 objc_msgSendSuper 方法

– (instancetype)init
{
self = [super init];
if (self) {

id obj1 = [self class];
id obj2 = [super class];
NSLog(@”%@”,obj1);
NSLog(@”%@”,obj2);

struct objc_super1 {
__unsafe_unretained id receiver;
Class superClass;
};
struct objc_super1 obj_super = {self,class_getSuperclass(object_getClass(self))};
id obj3 = objc_msgSendSuper(&obj_super,@selector(class));

}
return self;
}

obj3输出的结果和直接调用 [super class]结果是一模一样的,刚才我们假设的情况都是 super Person 没有 class 方法,如果 Person 重写了 class 方法呢,将会怎么样?

当我们在 Student init 方法中调用 [super class] 时候 ,它首先会到 Person 类中查找 class 方法 ,当它发现了 Person 实现了 class 方法,就会调用[person class] 方法, object_getClass 这个时候 object_getClass(self), 这个 self 是 Student 的实例对象,就是消息接收者,所以即使重写了 Person 的 class 方法 ,依然返回的还是 Student ,除非来个*端的 把 class 方法 实现 返回个 nil , 这样*终调用[super class]结果才会返回nil

KVO巧妙的利用了子类 重写了 class 方法 ,让我们误以为 [person class] 还是当前类 Person ,而不是动态创建的子类 NSKVONotifily_Person 这个子类 , 就是让子类返回了父类的 Class ,所以调用 [person class] 返回的还是 Person 从而对开发者隐藏了 NSKVONotifily_Person子类的存在

– (Class)class {
return class_getSuperclass(object_getClass(self));
}

iOS 数据结构之队列

队列是iOS 中常见的一种数据结构,比如 NSOpeartionQueue 和 GCD 的 各种队列 ,其特点是先进先出(First In First out), 在多线程执行执行多个任务时候 ,放进同一队列的任务是顺次从队列里取出任务并执行的.
gith 代码仓库 : https://github.com/ZhaoBingDong/iOS-DataStructures.git

队列在我们生活中也很常见,比如排队购票 去银行办理业务排队办理业务,都是队首的办理完业务后,离开柜台 下一个人接着办理业务

本文我们将介绍两种队列实现方式, 数组队列和循环队列.
数组队列是利用我们已经写好的数据结构 ArrayList 来存放元素,每次入队时候 往数组后边添加一个元素,每次出队时候从数组第0个元素位置出队一个元素,但是用数组队列其缺点是 在出队时候 数组里所有元素都要整体往前移动1个位置 .

数组和循环队列所有方法如下

+ (instancetype) arrayQueue;
+ (instancetype) arrayQueueWithCapacity:(NSInteger)capacity;
– (instancetype) initWithCapacity:(NSInteger)capacity;

– (void) enqueue:(ObjectType)obj; ///入队列
– (id) dequeue; ///出队列
– (void) removeAllObjects; ///移除队列里边所有元素

///firstObject
@property (nonatomic,weak) ObjectType firstObject;
///size
@property (nonatomic,assign,readonly) NSInteger size;
///isEmpty
@property (nonatomic,assign,getter=isEmpty) BOOL empty;

#import “ArrayList.h”
#import “ArrayQueue.h”

@interface ArrayQueue ()
{
ArrayList *_array;
}

@end

@implementation ArrayQueue

+ (instancetype)arrayQueue {
return [[ArrayQueue alloc] initWithCapacity:10];
}

+ (instancetype)arrayQueueWithCapacity:(NSInteger)capacity {
return [[ArrayQueue alloc] initWithCapacity:capacity];
}

– (instancetype)initWithCapacity:(NSInteger)numItems {
if (self = [super init]) {
_array = [ArrayList arrayWithCapacity:numItems];
}
return self;
}

– (void)enqueue:(id)obj {
[_array addObject:obj];
}

– (id)dequeue {
[_array removeObjectAtIndex:0];
return nil;
}

– (id)firstObject {
return [_array firstObject];
}

– (void)removeAllObjects {
[_array removeAllObjects];
}

– (NSInteger)size {
return _array.count;
}
– (BOOL)isEmpty {
return _array.count == 0;
}

– (NSString *)description {

NSMutableString *res = [NSMutableString string];
[res appendFormat:@”ArrayQueue: %p \n”,self];
[res appendString:@”front [ “];
for (int i = 0; i<_array.count; i++) {
id object = [_array objectAtIndex:i];
[res appendFormat:@”%@”,object];
if (i != _array.count – 1) {
[res appendString:@” , “];
}
}
[res appendString:@” ] tail “];
return res;
}

– (void)dealloc
{

}

@end

 

循环队列当出队一个元素后,不需要队列里所有元素往前移动,只需要移动队列 _front 和 _tail 指向的位置 , 可以复用已经出队元素留下的内存空间 ,类似与一个环形 , 其内部实现也是一个数组用来存放入队的元素, 当入队元素个数小于数组元素时 就会将元素放到一个空的内存空间位置,只需要维护下_front 和 _tail 指向位置 用来区分队首和队尾

#import “LoopQueue.h”

static NSInteger const defaultCapacity = 10;
typedef void * AnyObject;

@interface LoopQueue ()
{
@private
AnyObject *_array;
NSInteger _front , _tail;
NSInteger _size;
}

///capacity
@property (nonatomic,assign) NSInteger capacity;

@end

@implementation LoopQueue

– (instancetype)initWithCapacity:(NSInteger)capacity {
if (self = [super init]) {
_capacity = capacity + 1;
size_t size = sizeof(AnyObject) * _capacity;
_array = calloc(_capacity, size);
_front = 0;
_size = 0;
_tail = 0;
}
return self;
}

+ (instancetype)loopQueue {
return [[LoopQueue alloc] initWithCapacity:defaultCapacity];
}

+ (instancetype)loopQueueWithCapacity:(NSInteger)capacity {
return [[LoopQueue alloc] initWithCapacity:capacity];
}

– (id)dequeue {
if (self.isEmpty) {
@throw [NSException exceptionWithName:@”queue empty” reason:@”Cannot dequeue from an empty queue.” userInfo:nil];
return nil;
}
id object = (__bridge_transfer id)(_array[_front]);
_array[_front] = NULL;
_front = (_front + 1) % (_size);
_size–;
if (_size == self.size * 0.25 && self.size * 0.5 != 0) {
[self resize:self.size * 0.5];
}
return object;
}

– (void)enqueue:(id)obj {
if (!obj) {
@throw [NSException exceptionWithName:@”Object empty” reason:@”Object cannot a null object” userInfo:nil];
return;
}
if ((_tail + 1) % self.capacity == _front ) {
[self resize:self.capacity * 2];
}
_array[_tail] = (__bridge_retained AnyObject)obj;
_tail = (_tail + 1) % self.capacity;
_size ++;
}

– (void)removeAllObjects {

}

/**
对数组扩容

@param capacity 新的容量
*/
– (void) resize:(NSInteger)capacity {

_capacity = capacity + 1;
AnyObject *oldArray = _array;
AnyObject *newArray = calloc(_capacity,sizeof(AnyObject));
size_t size = sizeof(AnyObject) * self.size;
memcpy(newArray,oldArray,size); ///对旧的数组进行值的拷贝
_array = newArray;
_front = 0;
_tail = self.size;
if (oldArray != NULL) {
free(oldArray);
oldArray = NULL;
}

}

– (NSInteger)size {
return _size;
}

– (BOOL)isEmpty {
return (_front == _tail);
}

– (id)firstObject {
if (self.isEmpty) {
@throw [NSException exceptionWithName:@”queue empty” reason:@”Cannot dequeue from an empty queue.” userInfo:nil];
return nil;
}
AnyObject obj = _array[_front];
return (__bridge id)obj;
}

– (NSString *)description {
NSMutableString *res = [NSMutableString string];
[res appendFormat:@”ArrayQueue: %p \n”,self];
[res appendString:@”front [ “];
for (NSInteger i = 0; i <self.size;i++) {
id object = (__bridge id)(_array[i]);
if (!object) continue;
[res appendFormat:@”%@”,object];
if (i < self.size – 1) {
[res appendString:@” , “];
continue;
}
}
[res appendString:@” ] tail “];
return res;
}

– (void)dealloc
{
if (_array != NULL) {
NSInteger i = self.capacity – 1;
while (i >= 0) {
AnyObject *obj = _array[i];
if (obj != NULL)
CFRelease(obj);
i–;
}
free(_array);
}
}

@end

 

测试用例 由于数组队列需要出队和入队时候需要频繁的移动数组里元素索引位置 ,这个操作时耗时的 ,而循环队列只需要把新入队的元素 存放在一个空的位置即可 ,减少了对数组内部元素的移动的操作.因此循环队列比数组队列时间耗时更少 性能更好

(void) testStack {
_timeArray = [NSMutableArray array];

///10万次对比 NSMutableArray 和 ArrayList
int number = 100000;
Person *p = [Person new];
for (int i = 0; i<10; i++) {
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
ArrayQueue

ios 数据结构和算法

链表
1、链表

查找
插入
移除
2、栈(先进后出,尾部添加或删除元素)

push(入栈)
pop(出栈)
peek(获取顶部值)
3、队列(先进先出,尾部添加元素,头部删除元素)

enqueue(入队)
dequeue(出队)
peek(获取顶部值)
4、双链表(与链表区别在于,双向指针)

查找
插入
移除
5、双端队列(与栈和队列的区别,首尾都能添加元素,首尾均能出列)

enqueue(入队)
dequeue(出队)
peek(获取顶部值)
排序算法
冒泡排序
/*
* 冒泡排序,相邻两个对比,前者比后者大,交换位置
*
*/

-(void)bubbleSort{
NSMutableArray *array = [NSMutableArray arrayWithArray:@[@3,@44,@38,@5,@47,@15,@36,@26,@27,@2,@46,@4,@19,@50,@49]];
BOOL swapped = NO;
do {
swapped = NO;
for (int i = 1; i < array.count; i++) {
NSInteger num1 = [array[i-1] integerValue];
NSInteger num2 = [array[i] integerValue];
if (num1 >num2) {
[array replaceObjectAtIndex:i-1 withObject:[NSNumber numberWithInteger:num2]];
[array replaceObjectAtIndex:i withObject:[NSNumber numberWithInteger:num1]];
swapped = YES;
}
}
} while (swapped);
NSLog(@”冒的排序:%@”,array);
}

 

选择排序
/*
* 选择排序,标记*小值,循环比较,替换*小值
*
*/

-(void)selectionSort{
NSMutableArray *array = [NSMutableArray arrayWithArray:@[@3,@44,@38,@5,@47,@15,@36,@26,@27,@2,@46,@4,@19,@50,@49]];
NSInteger repeatTimes = array.count -1;
int sortBeginIndex = 0;//开始对比下标
do {
int miniNumIndex = sortBeginIndex;
NSInteger miniNum = [array[sortBeginIndex] integerValue];
for (int i = sortBeginIndex +1; i < array.count; i++) {
NSInteger selectNum = [array[i] integerValue];
if (selectNum < miniNum) {
miniNum = selectNum;
miniNumIndex = i;
}
}
[array replaceObjectAtIndex:miniNumIndex withObject:array[sortBeginIndex]];
[array replaceObjectAtIndex:sortBeginIndex withObject:[NSNumber numberWithInteger:miniNum]];
sortBeginIndex ++;
repeatTimes –;
} while (repeatTimes > 0);
NSLog(@”选择排序:%@”,array);
}

插入排序
/*
* 插入排序,逐个元素拿出来,与其左边元素逐个对比,碰到比该元素小的元素,则插入在对比元素后
*
*/

-(void)insertionSort{
NSMutableArray *array = [NSMutableArray arrayWithArray:@[@3,@44,@38,@5,@47,@15,@36,@26,@27,@2,@46,@4,@19,@50,@49]];
int sortBeginIndex = 1;//开始对比下标
do {
NSInteger sortBeginNum = [array[sortBeginIndex] integerValue];
[array removeObjectAtIndex:sortBeginIndex];
for (int i = sortBeginIndex -1; i >= 0; i–) {
NSInteger compareNum = [array[i] integerValue];
if (compareNum < sortBeginNum) {
[array insertObject:[NSNumber numberWithInteger:sortBeginNum] atIndex:i+1];
break;
}else{
if (i==0) {
[array insertObject:[NSNumber numberWithInteger:sortBeginNum] atIndex:0];
}
}
}
sortBeginIndex ++;
} while (sortBeginIndex < array.count);
NSLog(@”插入排序:%@”,array);
}

iOS开发——OC常用的数据结构一览。

一.     在iOS开发中常用的结构体

1.    NSRange-一个范围结构体,location是位置,length是长度;{4,5},NSMakeRange(4,5);NSStringFromRange可以把它当NSString*返回

2.    NSSize-由一个CGSize被typedef过来,CGFloat是double类型typedef过来的,也由两个值组成:with宽度,height高度;{21,19};也可以NSMakeSize(21,19);NSStringFromSize可以由它返回一个NSString*返回

3.    NSPoint-有一个CGPoint被typedef过来的,由两个值组成:一个x值,一个y值;{0,0}或者NSMakePoint(0,0);NSStringFromPoint可以把它返回一个NSString*

4.    NSRect-是CGRect被typedef过来,由一个CGPoint和一个CGSize组成;{0,0,100,200}或者NSMakeRect(0,0,100,200);NSStringFromRect返回一个描述它的String*

二.     基本数据类型的包装类

1.    什么是基本数据类型的包装类:NSNumber它是一个类,继承于NSValue,可以用numberWithxxx把xxx基本类型入参返回一个NSSNumber类型的指针,可以用%@打印NSNumber;也可以用xxxValue方法把当前NSNumber返回一个xxx类型的基本数据

2.    好处:可以直接转字符串不用记住那么多打印的%格式;

3.    数组,集合中不允许存储基本数据类型,如果想让数组,集合中存储基本数据类型,就必须要把基本数据类型包装成NSNumber类

三.     集合类

1.    NSArray,NSDictionary,NSSet都是用来打包数据的,统称为集合类

2.    他们会有增删改查的基本功能,也都是类对象

3.    NSArray不可变,NSMutableArray可变;不可以在NSArray中存储nil,因为nil代表结束;

4.    NSArray实际上存储的是地址,也可以继续存储NSArray*,也就变成类一个二维表,每次打印NSArray时都是调用内部对象的description方法

5.    如果要在数组中存储基本数据类型一定要包装为NSNumber,否则会存储进乱码

6.    2013年新加入了@[]方式初始化一个NSArray,也可以快速获取NSArray中的对象array[index];

7.    #pragma mark xxx就可以在.m文件定义一个标记,可以快速定位到标记的地方

8.    初始化一般用initWithObjects:,而获取一般用objectAtIndex:方法

9.    count方法可以获得数组中元素的个数

10. 判断NSArray中是否存在某个对象:containsObject:返回BOOL

11. 增强for循环:for(NSObject*obj in array){}

12. 在实际的开发当中,单个数组一般都是只用来存放一种对象,因为遍历数组时如果调用某个对象特有的方法时会引起异常

13. 枚举器:objectEnumerator方法返回一个NSEnumerator类型的对象,也就是我们的枚举器对象。while(value = [enumerator nextObject]){}当nextObject在NSArray的*后一个对象为nil时就不再执行了

14. NSArray的排序:1使用sortedArrayUsingSelector,[arraysortedArrayUsingSelector:@selector(compare:)];     2使用block排序,[arraysortedArrayUsingComparator:^NSComparisonResult: (id obj1, obj2){return[obj1compare: obj2]}];obj1,obj2反过来就是倒叙;

15. 自定义类数组的排序:如果你想对自己定义的对象所存的数组进行排序,要新建一个NSSortDescriptor对象,NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey: @”age”ascending:YES];其中@”age”就是你想要排序的主键,在把NSSortDescriptor对象放入一个NSArray中,之后调用[array sortedArrayUsingDescriptors:数组]为什么是数组呢?因为它支持多字段排序,前面的键优先级高。

16. 其实用block排序同样可以实现对自定义类数组的排序,比较方法要自己在block中写

17. NSMutableArray,同样是继承自NSArray;同样有增删改查addObject:obj,insertObject:obj atIndex:index,removebject:obj会删除数组中所有同地址对象。还有removeObjectAtIndex:index方法,注意index如果超出范围就会导致异常

18. 数组中可以存储同一个地址多次

19. removeAll是清空整个数组

20. 在遍历可变数组时,千万不要更改数组的count,否则可能引起程序错误或者引发异常

21. 如果非要删除数组中的对象,那么可以先声明一个NSMutableArray,在遍历NSArray同时删除NSMutableArray中的对象就可以了;

22. NSDictionary,字典类,不可变;

23. 初始化字典:[dictionaryWithObjectsWithKeys:@”value”,@”key”…,nil],也可以@{@”key1”:@”value1”,@”key2”:@”value2”,…};

24. 字典中可以存放任意的数据类型,基础数据类型同样要求封装为NSNumber之后存储

25. 字典中的顺序不和数组顺序一样时自然顺序

26. 字典内同样可以存放字典类型,类似NSArray,其实字典中存放的也是对象的指针存放的地址

27. 字典一样有count,一样是字典中字典项的个数

28. 取值,[dictobjectForKey:@”key”],键对应的值是什么类型就用什么类型的指针去接收即可

29. 遍历方式,首先要取出所有key,然后遍历key组成的NSArray,在循环体之中用[objectForKey:@”key”]单独获取当前遍历到的value即可实现

30. 如果你的字典中存储类多种不同类型,那么注意点和NSArray几乎一样

31. NSMutableDictionary,继承自NSDictionary,可以变换的字典;

32. 有增删改查,[setObject:objforKey:key]为key设置obj的值,[removeObjectForKey:key]方法用来删除给定key键的obj对象值,[removeAllObjects]清空字典;

33. 遍历,普通for,增强for,枚举器while遍历同上,都是类似的。

34. NSSet*大的功能就是它不可以存储相同的对象多次,同样不可变

35. NSMutableSet,可变集合,继承自NSSet。addObject:添加元素,removeObject:删除元素removeAllObject清空集合

36. 遍历NSMutableSet,NSEnumerator* en = [muset objectEnumerator];然后用nextObject实现遍历

37. 集合类之间的相互转换:NSXxx转换NSMutableXxx,xxxWithXxx:方法就可以实现

38. NSDictionary à NSArray:allKeys,allValues分别生成俩数组

39. 字符串转换成NSArray:componentsSeparatedByString:@”.”方法

40. 集合的手动内存管理:当你把对象存入到数组中的时候,数组会对这个对象进行一次retain操作,在数组removeObject:的时候会对这个对象进行一次release操作,使得retainCount正常。同样的,清空数组会对数组内的每个元素发送release消息。调用数组的release方法也是会对每个元素发送release消息

41. ARC机制下的集合内存管理:当你把对象存入到数组中时,它对对象进行一次strong指针保存,当数组removeObject或者removeAllObjects时会释放这个strong指针。同样的道理,数组调用= nil的时候,也同样会释放所有的strong指针。这样当元素 = nil的时候系统就自动回收元素了;

42. 所以ARC机制之中还是要担心内存溢出了

四.     文件管理器

1.    文件操作:NSFileManager,文件管理器,通过创建使用这个对象的方法来进行文件操作。

2.    NSFileManager * file =[NSFileManager defaultManager];获得文件管理器对象,且它是单实例模式。

3.    如果要模仿这种单实例的话,要在类方法中实现一个静态变量static NSObj* instance = nil;这个静态变量会在main函数结束才销毁,defaultObj方法只需要判断如果这个静态变量为nil就新建一个,否则直接返回这个静态变量即可;

4.    用%p可以打印对象的指针地址,同样NSFileManager也可以alloc,init来创建一个新的NSFileManager;

5.    获取文件属性:获取NSFileManager对象,&error入参来获取错误信息,然后通过attributesOfItemXxx:方法返回一个NSDictionary对象,里面是返回的文件信息;

6.    获取文件中的子集:contentsOfDirectoryAtPath:方法获取路径下的含有的子文件,只能够获得目标目录下一级的目录!如果想要打出给定路径下包含的所有子文件以及子文件内的文件,要用subpathsOfDirectoryAtPath:方法实现。

7.    创建一个文件夹:调用NSFileManager对象的createDictionaryAtPath:方法就是可以实现创建,第二个参数会逐级的创建文件夹,如果为No的话没有上级目录则会报错

8.    如何移动目录:调用NSFileManager对象的moveItemAtPath: toPath:方法实现

9.    删除目录:调用NSFileManager对象的removeItemAtPath:方法实现

10. 复制文件:调用NSFileManeger对象的copyItemAtPath:toPath:方法实现

11. 只要是文件,不论格式基本都可以使用NSData对象接收

 

五.     数据NSData

1.    NSData是你要加载不同格式文件时要使用的数据类型,这是专门用来处理文件数据的数据类型

2.    NSMutableData,可变数据类,继承自NSData类,用法类似于NSData

六.     日期操作

1.    NSDate,日期数据类型,初始化直接date方法就可以

2.    日期的比较:首先NSTimeInterval其实是一个double类型,可以在当前日期调用addTimeInterval:方法加上这个数据类型获得一个相差这么长时间段的NSDate对象,对了,NSTimeInterval是以秒计数;如果要减去就加上一个负的NSTimeInterval

3.    isEqualToDate:是否相同;earlierDate:比较早的时间;laterDate:比较晚的时间;

4.    格式化日期:NSDateFormatter就是一个日期格式化对象数据类型,调用NSDateFormatter的setDateFormat:方法。例如:@”yyyy/MM/dd hh:mm:ss”让后用NSString的stringFromDate方法即可

5.    这个格式化的hh代表12时制,HH代码24时制

6.    处理时区问题:调用NSDateFormatter的setTimeZone:方法计算时区导致的时差

IOS计算MD5值

#import <Foundation/Foundation.h>

#import <CommonCrypto/CommonDigest.h>

#define FileHashDefaultChunkSizeForReadingData 1024*8 // 8K

@interface QQYMD5Util : NSObject

//计算NSData的MD5值

+(NSString*)getMD5WithData:(NSData*)data;

//计算字符串的MD5值,

+(NSString*)getmd5WithString:(NSString*)string;

//计算大文件的MD5值

+(NSString*)getFileMD5WithPath:(NSString*)path;

@end

#import “QQYMD5Util.h”

@implementation QQYMD5Util

+ (NSString*)getmd5WithString:(NSString *)string

{

const char* original_str=[string UTF8String];

unsigned char digist[CC_MD5_DIGEST_LENGTH]; //CC_MD5_DIGEST_LENGTH = 16

CC_MD5(original_str, strlen(original_str), digist);

NSMutableString* outPutStr = [NSMutableString stringWithCapacity:10];

for(int  i =0; i<CC_MD5_DIGEST_LENGTH;i++){

[outPutStr appendFormat:@”%02x”, digist[i]];//小写x表示输出的是小写MD5,大写X表示输出的是大写MD5

}

return [outPutStr lowercaseString];

}

+ (NSString*)getMD5WithData:(NSData *)data{

const char* original_str = (const char *)[data bytes];

unsigned char digist[CC_MD5_DIGEST_LENGTH]; //CC_MD5_DIGEST_LENGTH = 16

CC_MD5(original_str, strlen(original_str), digist);

NSMutableString* outPutStr = [NSMutableString stringWithCapacity:10];

for(int  i =0; i<CC_MD5_DIGEST_LENGTH;i++){

[outPutStr appendFormat:@”%02x”,digist[i]];//小写x表示输出的是小写MD5,大写X表示输出的是大写MD5

}

 

//也可以定义一个字节数组来接收计算得到的MD5值

//    Byte byte[16];

//    CC_MD5(original_str, strlen(original_str), byte);

//    NSMutableString* outPutStr = [NSMutableString stringWithCapacity:10];

//    for(int  i = 0; i<CC_MD5_DIGEST_LENGTH;i++){

//        [outPutStr appendFormat:@”%02x”,byte[i]];

//    }

//    [temp release];

 

return [outPutStr lowercaseString];

 

}

+(NSString*)getFileMD5WithPath:(NSString*)path

{

return ( NSString *)FileMD5HashCreateWithPath((__bridge CFStringRef)path,FileHashDefaultChunkSizeForReadingData);

}

CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath,

size_t chunkSizeForReadingData) {

 

// Declare needed variables

CFStringRef result = NULL;

CFReadStreamRef readStream = NULL;

 

// Get the file URL

CFURLRef fileURL =

CFURLCreateWithFileSystemPath(kCFAllocatorDefault,

(CFStringRef)filePath,

kCFURLPOSIXPathStyle,

(Boolean)false);

 

CC_MD5_CTX hashObject;

bool hasMoreData = true;

bool didSucceed;

 

if (!fileURL) goto done;

 

// Create and open the read stream

readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault,

(CFURLRef)fileURL);

if (!readStream) goto done;

didSucceed = (bool)CFReadStreamOpen(readStream);

if (!didSucceed) goto done;

 

// Initialize the hash object

CC_MD5_Init(&hashObject);

 

// Make sure chunkSizeForReadingData is valid

if (!chunkSizeForReadingData) {

chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData;

}

 

// Feed the data to the hash object

while (hasMoreData) {

uint8_t buffer[chunkSizeForReadingData];

CFIndex readBytesCount = CFReadStreamRead(readStream,

(UInt8 *)buffer,

(CFIndex)sizeof(buffer));

if (readBytesCount == -1)break;

if (readBytesCount == 0) {

hasMoreData =false;

continue;

}

CC_MD5_Update(&hashObject,(const void *)buffer,(CC_LONG)readBytesCount);

}

 

// Check if the read operation succeeded

didSucceed = !hasMoreData;

 

// Compute the hash digest

unsigned char digest[CC_MD5_DIGEST_LENGTH];

CC_MD5_Final(digest, &hashObject);

 

// Abort if the read operation failed

if (!didSucceed) goto done;

 

// Compute the string result

char hash[2 *sizeof(digest) + 1];

for (size_t i =0; i < sizeof(digest); ++i) {

snprintf(hash + (2 * i),3, “%02x”, (int)(digest[i]));

}

result = CFStringCreateWithCString(kCFAllocatorDefault,

(const char *)hash,

kCFStringEncodingUTF8);

 

done:

 

if (readStream) {

CFReadStreamClose(readStream);

CFRelease(readStream);

}

if (fileURL) {

CFRelease(fileURL);

}

return result;

}

@end

调用事例:

#include “QQYMD5Util.h”

NSString* filePath;

NSString* fileMD5 = [QQYMD5Util getFileMD5WithPath:filePath];

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速