iOS

RuntimeError - [Xcodeproj] Unknown object version.

今天遇到 CocoaPods 不能使用了,提示:RuntimeError - [Xcodeproj] Unknown object version.

经过百度+谷歌之后还是不能解决,然后就想着重装吧,重装一般能解决大部分问题的。使用命令如下:

sudo gem uninstall cocoapods -n /usr/local/bin
sudo gem install cocoapods -n /usr/local/bin

Done !

总结:如果遇到问题很久都没有头绪,可以尝试从 0 开始。

解决 iTunes 12.7 无法下载 AppStore 应用的 IPA 包问题

问题描述

苹果 iTunes 12.7 已经无法下载 ipa 包,如果想在 iTunes 12.7 使用已经无力回天。所以,只能降级!降级!降级!

注:降级 iTunes 12.6.2 会导致无法识别到 iPhone8 / iPhone8 plus / iPhone X 设备, 2017.10月30号更新,下载 iTunes 12.6.3 支持 iPhone 8,iPhone X和iOS 11。

降级步骤

  • 下载 12.6.3 安装包
  • 按步骤进行安装刚才下载的安装包

错误处理

iTunes安装完之后显示:不能读取文件‘iTunes Library.itl'',说因为它是由更高版本iTunes所创建的。你要现在下载 iTunes 吗?

到 Music/iTunes 目录下,删除 iTunes Library.itl 目录就可以,重启打开就正常

rm -rf ~/Music/iTunes/iTunes\ Library.itl

文章转载自: 解决 iTunes 12.7 无法下载 AppStore 应用的 IPA 包问题

如何删除 Xcode 的描述文件

删除路径:
/Users/电脑名/Library/MobileDevice/Provisioning Profiles

// Xcode 16
~/Library/Developer/Xcode/UserData/Provisioning\ Profiles

或者在该路径下找到对应的删除之.

查看 .ipa 包所包含的 UDID

如何查看打出来的 .ipa 包是否包含某个 UDID?

  1. 首先把.ipa包解压缩,找到对应的包
  2. 查看包里的内容,找到对应的证书文件,名字为:embedded.mobileprovision
  3. 打开终端,找到embedded.mobileprovision所在的目录,然后运行命令行: security cms -D -i embedded.mobileprovision
  4. 查看 ProvisionedDevices对应的数组

当然,你把 .ipa 包上传到蒲公英后,也是可以看到的^_^

PS : 如果 .ipa 包里不包含 某个 UDID ,则需要通过开发者账号在 Device 里添加 一个“UDID”,或者通过 Xcode 注册一个设备,然后删除 Xcode 的 Provisioning Profiles 目录下的对应文件,重启 Xcode并进行打包。

命令:

rm -rf ~/Library/MobileDevice/Provisioning\ Profiles/*

谁动了我的 plist

在《破解Revealapp的试用时间限制》这篇blog里,笔者在提供补丁之前,曾经提供了一种通过修改plist中的安装时间来延长试用的方法。这个方法对很多留言的同学来说不起作用,当初笔者也没有深究,就继而提供了补丁来直接修改可执行文件。不过这些同学的修改plist不起作用的这个问题却一直留在笔者心中。直到最近,看到一篇文章(参考资料1),才恍然大悟。

原来在OSX的10.9版本之前,plist的读写都是APP自己来进行的,但升级到10.9的时候,有一个专门的精灵进程来负责plist文件的读写,那就是cfprefsd。

如上图所示,所有的plist读写都是通过进程cfprefsd来进行的,也就是说cfprefsd会对plist数据信息缓存。这就是为什么即使你修改了plist,APP读到的仍然是以前的数据。

要解决这个问题可以用如下步骤:

  1. 退出plist对应的APP
  2. 在终端中运行killall cfprefsd杀掉所有的cfprefsd进程
  3. 修改plist
  4. 再运行APP,此时新的plist就会起作用了

参考资料:

How-to: Replace preference files in Mavericks

谁动了我的 plist

iOS 获取图片的原始格式

今天测试给过来一张图片(后缀是.png)说无法在 APP 的 WebView 里面无法显示,而且在 Safari 里也是无法打开的,但在谷歌浏览器上是可以正常显示。起初是知道 WebP 格式的图片苹果是不支持显示的,但这个图片的后缀是.png 的,难道还有 png 的图片是苹果不支持的么?

根据个人经验,是没有听说苹果不支持 png 格式的图片的,这时想到以前自己更改 JPG 图片后缀的事情,是不是这张图片也是经过别人手动改后缀的呢,带着这个疑问,我决定手动判断这张图片的原始格式。

记得以前在看 SDWebImage 源码时,源码中是有关于判断根据 image 来判断图片的实际格式的,于是从 SDWebImage 中的源码中抠出来判断图片实际格式的代码。

图片的前8位是存储图片格式的,可以通过先读取图片的数据,拿到图片的前8位来判断图片的类型
/**
 *
 * 根据图片数据获取图片的原始类型
 * 
 * @param data 图片的二进制数据
 * @return 图片的实际格式
 */
- (NSString *)typeForImageData:(NSData *)data {
    uint8_t c;
    [data getBytes:&c length:1];
    
    switch (c) {
        case 0xFF:
            return @"image/jpeg";
        case 0x89:
            return @"image/png";
        case 0x47:
            return @"image/gif";
        case 0x49:
        case 0x4D:
            return @"image/tiff";
        case 0x52: {
            //R as RIFF for WEBP
            if (data.length < 12) {
                return nil;
            }
            NSString *identifierTypeStr = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([identifierTypeStr hasPrefix:@"RIFF"] && [identifierTypeStr hasSuffix:@"WEBP"]) {
                return @"image/webp";
            }
            return nil;
        }
            
        default:
            break;
    }
    return nil;
}

That's All.

Code

原文链接:iOS 获取图片的原始格式

XCode 8.0下 NSLog 打印不完全

今天在重新整理希尔排序的时候,使用了 100000 个种子数据进行测试,但发现在排序好后,用 NSLog 无法打印完全排序结果,开始以为自己的排序算法写法有误,但查看内存数据信息,显示排序结果正常。这时怀疑 NSLog 在 XCode 8.0 下可能有 Bug。 于是使用 c 语言函数 printf 进行打印。

Bingo !

代码如下:

NSMutableArray<NSNumber *> *results = [dataList mutableCopy];
printf("%s", [results.description UTF8String]);

附(NSString 与 char 相互转换):

//NSString转换char
NSString * str1= @"Test";
const char * c1 =[str1 UTF8String];
//char转换NSString
const char * c2 ="test";
NSString *str2 = [NSString stringWithUTF8String:c2];

原文链接:XCode 8.0下 NSLog 打印不完全

iOS 位枚举

在 iOS 开发中,我们使用系统的枚举定义的时候,经常可以看到位枚举

typedef NS_OPTIONS(NSUInteger, UIControlState) {
    UIControlStateNormal       = 0,
    UIControlStateHighlighted  = 1 << 0,                  // used when UIControl isHighlighted is set
    UIControlStateDisabled     = 1 << 1,
    UIControlStateSelected     = 1 << 2,                  // flag usable by app (see below)
    UIControlStateFocused NS_ENUM_AVAILABLE_IOS(9_0) = 1 << 3, // Applicable only when the screen supports focus
    UIControlStateApplication  = 0x00FF0000,              // additional flags available for application use
    UIControlStateReserved     = 0xFF000000               // flags reserved for internal framework use
};

需要掌握位枚举,我们需要先了解位运算位移

位运算

位运算有两种:位或(|)位与(&)

  • 位或(|) :两个进行或(|)运算。运算规则:两个运算的只要有一个为1则运算结果为1,否则为0

如:
0000 0001 | 0000 0010 结果为:0000 0011
0000 0000 | 0000 0000 结果为:0000 0000

  • 位与(&) :两个进行与(&)运算。运算规则:两个运算的都为1则运算结果为1,否则为0

如:
0000 0001 & 0000 0001 结果为:0000 0001
0000 0001 & 0000 0010 结果为:0000 0000

位移

位移包含两种:左移(<<)右移(>>)

  • << :将一个数的二进制位向左移动 n 位,高位丢弃,低位补 0。如将数字1(0000 0001)左移两位得到结果为:4(0000 0100)。表述为:1 << 2。

    左移就是将一个数乘以 2 的 n 次方。
  • >> :将一个数的二进制位向右移动 n 位,低位丢弃,高位补 0。如将数字4(0000 0100)右移两位得到结果为:1(0000 0001)。表述为:4 >> 2。

    右移就是将一个数除以 2 的 n 次方。

iOS 位枚举

我们有如下定义:

typedef NS_ENUM(NSUInteger, HJDirection) {
    // 0000 0001
    HJDirectionLeft = 1 << 0,
    // 0000 0010
    HJDirectionRight = 1 << 1,
    // 0000 0100
    HJDirectionTop = 1 << 2,
    // 0000 1000
    HJDirectionBottom = 1 << 3
};
PS:定义一个位枚举时,我们通常以一个数字作为基准,如数字1,然后对该数字进行左移(右移),这样我们才能在后面使用中判断是否包含某个枚举值。

使用:

//获取所有方向(位或运算)
//0000 1111
HJDirection directionAll = HJDirectionLeft | HJDirectionRight | HJDirectionTop | HJDirectionBottom;

//获取是否包含某个方向(位与运算)
if ((directionAll & HJDirectionLeft) == HJDirectionLeft) {
    //0000 0001
    NSLog(@"满足条件:左方向");
}
if ((directionAll & HJDirectionRight) == HJDirectionRight) {
    //0000 0010
    NSLog(@"满足条件:右方向");
}
if ((directionAll & HJDirectionTop) == HJDirectionTop) {
    //0000 0100
    NSLog(@"满足条件:上方向");
}
if ((directionAll & HJDirectionBottom) == HJDirectionBottom) {
    //0000 1000
    NSLog(@"满足条件:下方向");
}

我们回到开始的 UIControlState 枚举定义

我们在定义 UIButton 状态时,一般会用到 UIControlStateNormalUIControlStateHighlightedUIControlStateSelected,但我们怎么去定义一个选中状态下的高亮呢?如果我们单纯的使用 UIControlStateHighlighted, 我们得到的只是默认状态下的高亮显示。这时我们就需要用到位枚举的神奇之处了。

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:@"点击我" forState:UIControlStateNormal];
//定义普通状态文字颜色
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//定义选中状态文字颜色
[button setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
//定义普通状态高亮文字颜色
[button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
//定义选中状态高亮文字颜色
[button setTitleColor:[UIColor orangeColor] forState:UIControlStateSelected | UIControlStateHighlighted];

Done !

Link:iOS 位枚举

iOS 关联,objc_getAssociatedObject, objc_setAssociatedObject

关联是指把两个对象相互关联起来,使得其中的一个对象作为另外一个对象的一部分。

CategoryAssociate作为Objective-C的扩展机制的两个特性,Category即分类,可以通过它来扩展方法;Associate,可以通过它来扩展属性;在iOS开发中,可能Category比较常见,相对的Associate,就用的比较少,要用它必须使用<objc/runtime.h>的头文件,然后就可以自由使用objc_getAssociatedObject以及objc_setAssociatedObject
在类的定义之外为类增加额外的存储空间

使用关联,我们可以不用修改类的定义而为其对象增加存储空间。这在我们无法访问到类的源码的时候或者是考虑到二进制兼容性的时候是非常有用。

关联是基于关键字的,因此,我们可以为任何对象增加任意多的关联,每个都使用不同的关键字即可。关联是可以保证被关联的对象在关联对象的整个生命周期都是可用的(在垃圾自动回收环境下也不会导致资源不可回收)。

创建关联

创建关联要使用到Objective-C的运行时函数:objc_setAssociatedObject 来把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象关键字关联的对象和一个关联策略。当然,此处的关键字和关联策略是需要进一步讨论的。

  • 关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
  • 关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。

下面的代码展示了如何把一个字符串关联到一个数组上。

NSArray *array = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
NSString *overValue = @"overCharValue";
static char overKey;
objc_setAssociatedObject(array, &overKey, overValue, OBJC_ASSOCIATION_RETAIN);
//通过对array的强引用,我们可以在其他地方根据关键字来获取关联对象的值,直到array被销毁
获取相关联的对象

获取相关联的对象时使用Objective-C函数 objc_getAssociatedObject。接着上面的代码,我们可以使用如下代码来获取与array相关联的字符串:

NSString * associatedObject = (NSString *)objc_getAssociatedObject(array, &overKey);
断开关联

断开关联是使用objc_setAssociatedObject函数,传入nil值即可。

我们可以使用如下的代码来断开字符串overview和arry之间的关联:

objc_setAssociatedObject(array, &overKey, nil, OBJC_ASSOCIATION_RETAIN);

其中,被关联的对象为nil,此时关联策略也就无关紧要了。

使用函数objc_removeAssociatedObjects可以断开所有关联。这个函数的主要目的是很容易的让对象恢复成它“原始状态”,你不应该使用它来移除关联的对象,因为它也会移除掉包括其他地方加入的全部的关联对象。所以一般你只需要通过调用objc_setAssociatedObject并传入nil值来清除关联值。

完整代码:

#import "HJViewController.h"
#import <objc/runtime.h>

@interface HJViewController ()

@property (nonatomic, copy) NSArray *array;

@end

@implementation HJViewController

static char overKey;
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSArray *array = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
    NSString *overValue = @"overValue";
    objc_setAssociatedObject(array, &overKey, overValue, OBJC_ASSOCIATION_RETAIN);
    self.array = array;
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    
//   objc_removeAssociatedObjects(self.array);
//   objc_setAssociatedObject(self.array, &overKey, nil, OBJC_ASSOCIATION_RETAIN);
    NSLog(@"%@", objc_getAssociatedObject(self.array, &overKey));
}

参考文章:iOS关联,objc_getAssociatedObject, objc_setAssociatedObject