UIImagePickController打開閃光模式拍照瞬間鎖屏crash

  • 復(fù)現(xiàn)步驟:
    在UIImagePickController拍照頁面開啟閃光模式推姻,在拍照的瞬間點(diǎn)擊鎖屏按鈕,App重回前臺后拍出來的照片一片漆黑,這時點(diǎn)擊”使用照片”會crash。
美團(tuán).gif
微信.gif
  • 系統(tǒng) < iOS 10

  • 原因:NSMutableDictionary setObject是參數(shù)傳nil導(dǎo)致的空免。

    注意:當(dāng)UIImagePickController的allowEditing屬性為YES的時候不會crash。

  • 解決方案:hook底層拍照處理API盆耽,當(dāng)發(fā)現(xiàn)拍出來的照片為空時手動生成一張空白圖片蹋砚。

通過Hopper查看產(chǎn)生這個問題有兩處可能返回nil的調(diào)用扼菠,

    1. -[PLPhotoTileViewController _newOriginalImageForPickerFromCachedData]; OC方法
void * -[PLPhotoTileViewController _newOriginalImageForPickerFromCachedData](void * self, void * _cmd) {
       rbx = self;
       rax = [self unscaledImage];
       if (rax == 0x0) {
               rax = [rbx image];
       }
       rax = _NewUIImageFromCachedImage(rax);
       return rax;
}
    1. int _CreateImageDataFromJPEGDataAndOrientation(int arg0, int arg1); C函數(shù)
   int _CreateImageDataFromJPEGDataAndOrientation(int arg0, int arg1) {
       rbx = PLExifOrientationFromImageOrientation(arg1, arg1);
       r15 = [NSDictionary alloc];
       rdx = [NSNumber numberWithInt:rbx];
       rbx = [r15 initWithObjectsAndKeys:rdx];
       r14 = CGImageCreateEXIFJPEGData(0x0, arg0, 0x0, rbx);
       [rbx release];
       rax = r14;
       return rax;
}

我們利用fishhook來對C函數(shù)進(jìn)行hook。

  • Code:
#import <fishhook/fishhook.h>
#import <objc/runtime.h>

#if __has_feature(objc_arc)
#error This file must be compiled with MRC. Use -fno-objc-arc flag (or convert project to MRC).
#endif

typedef id (*ImageDataIMP)(id, SEL, ...);

static CGImageRef (*orig_createImageData)(NSData *data, UIImageOrientation orientation);
UIImage *createBlankImage();

CGImageRef new_createImageData(NSData *arg0, UIImageOrientation arg1)
{
    CGImageRef imageRef = orig_createImageData(arg0, arg1);
    if (imageRef == NULL) {
        UIImage *image = createBlankImage();
        imageRef = CGImageRetain(image.CGImage);
        [image release];
    }
    return imageRef;
}


NSString *originCreateImageDataFromJPEGDataAndOrientationFuncKey()
{
    //CreateImageDataFromJPEGDataAndOrientation
    static NSString *key;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        key = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char [])
                                              {0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x4a, 0x50, 0x45, 0x47, 0x44, 0x61, 0x74, 0x61, 0x41, 0x6e, 0x64, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e} length:41]
                                    encoding:NSASCIIStringEncoding];
    });
    return key;
}

NSString *originalImageForPickerFromCachedDataSELKey()
{
    //_newOriginalImageForPickerFromCachedData
    static NSString *key;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        key = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char [])
                                              {0x5f, 0x6e, 0x65, 0x77, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x61,
                                                  0x67, 0x65, 0x46, 0x6f, 0x72, 0x50, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x46, 0x72, 0x6f,
                                                  0x6d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61} length:40]
                                    encoding:NSASCIIStringEncoding];
    });
    return key;
}

@implementation UIImagePickerController (DEFImagePickerControllerCrashFix)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        //先手動加載私有framework都弹,否則取不到私有類
        NSBundle *photoLibraryBundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/PhotoLibrary.framework"];
        if (![photoLibraryBundle load]) {
            return;
        }
        
        //PLPhotoTileViewController
        NSString *photoTileControllerKey = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char [])
                                                                           {0x50, 0x4c, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x54, 0x69,
                                                                               0x6c, 0x65, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x74,
                                                                               0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72} length:25] encoding:NSASCIIStringEncoding];
        
        Class originCls = NSClassFromString(photoTileControllerKey);
        [photoTileControllerKey release];
        Class overridedCls = self;
        
        SEL originalSelector = NSSelectorFromString(originalImageForPickerFromCachedDataSELKey());
        SEL overrideSelector = @selector(p_newOriginalImageForPickerFromCachedData);
        
        Method originalMethod = class_getInstanceMethod(originCls, originalSelector);
        Method overrideMethod = class_getInstanceMethod(overridedCls, overrideSelector);
        
        BOOL success = class_addMethod(originCls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod));
        if (success) {
            class_replaceMethod(overridedCls, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, overrideMethod);
        }
        
        rebind_symbols((struct rebinding[1]){originCreateImageDataFromJPEGDataAndOrientationFuncKey().UTF8String, new_createImageData, (void *)&orig_createImageData}, 1);
    });
}

- (id)p_newOriginalImageForPickerFromCachedData
{
    SEL originalSelector = @selector(p_newOriginalImageForPickerFromCachedData);
    Method method = class_getInstanceMethod([UIImagePickerController class], originalSelector);
    ImageDataIMP imp = (ImageDataIMP)method_getImplementation(method);
    UIImage *image = imp(self, _cmd);
    if (!image) {
        //如果沒有生成照片娇豫,返回一張空白圖片,防止crash
        image = createBlankImage();
    }
    return image;
}

@end

//生成一張黑色圖片
UIImage *createBlankImage()
{
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:[[UIScreen mainScreen] bounds]];
    UIImage *blackImage = [UIImage p_imageWithColor:[UIColor blackColor] path:bezierPath];
    CGImageRef imageRef = blackImage.CGImage;
    UIImage *drawImage = [[UIImage alloc] initWithCGImage:imageRef
                                                    scale:[UIScreen mainScreen].scale
                                              orientation:UIImageOrientationUp];
    return drawImage;
}

@interface UIImage (PickerBlankImage)
+ (UIImage *)p_imageWithColor:(UIColor *)color path:(UIBezierPath *)path;
@end

@implementation UIImage (PickerBlankImage)

+ (UIImage *)p_imageWithColor:(UIColor *)color path:(UIBezierPath *)path
{
    CGRect rect = CGRectMake(0, 0, 3, 3);
    if (path) {
        rect = path.bounds;
    }
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    if (path) {
        [path fill];
    } else {
        CGContextFillRect(context, rect);
    }
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畅厢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子氮昧,更是在濱河造成了極大的恐慌框杜,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袖肥,死亡現(xiàn)場離奇詭異咪辱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)椎组,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門油狂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寸癌,你說我怎么就攤上這事专筷。” “怎么了蒸苇?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵磷蛹,是天一觀的道長。 經(jīng)常有香客問我溪烤,道長味咳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任檬嘀,我火速辦了婚禮槽驶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸳兽。我一直安慰自己掂铐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布贸铜。 她就那樣靜靜地躺著堡纬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒿秦。 梳的紋絲不亂的頭發(fā)上烤镐,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音棍鳖,去河邊找鬼炮叶。 笑死碗旅,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的镜悉。 我是一名探鬼主播祟辟,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侣肄!你這毒婦竟也來了旧困?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤稼锅,失蹤者是張志新(化名)和其女友劉穎吼具,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矩距,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拗盒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锥债。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陡蝇。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哮肚,靈堂內(nèi)的尸體忽然破棺而出登夫,到底是詐尸還是另有隱情,我是刑警寧澤绽左,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布悼嫉,位于F島的核電站,受9級特大地震影響拼窥,放射性物質(zhì)發(fā)生泄漏戏蔑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一鲁纠、第九天 我趴在偏房一處隱蔽的房頂上張望总棵。 院中可真熱鬧,春花似錦改含、人聲如沸情龄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骤视。三九已至,卻和暖如春鹃觉,著一層夾襖步出監(jiān)牢的瞬間专酗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工盗扇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祷肯,地道東北人沉填。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像佑笋,于是被迫代替她去往敵國和親翼闹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 原文鏈接http://www.cnblogs.com/kenshincui/p/4186022.html 音頻在i...
    Hyman0819閱讀 21,722評論 4 74
  • 原文地址 如何去衡量一款應(yīng)用的質(zhì)量好壞停撞?為了回答這一問題瓷蛙,APM這一目的性極強(qiáng)的工具向開發(fā)順應(yīng)而生。最早的APM開...
    sindri的小巢閱讀 4,870評論 2 44
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉戈毒,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,732評論 0 9
  • 抽空回老家艰猬,見老友,是聚會埋市,也算散心冠桃! 和小學(xué)同學(xué)的碰面是意料之外的!原本是等一個常在聯(lián)系的友人道宅,結(jié)果她居然帶了一...
    兮兮0225閱讀 279評論 0 0
  • 如果存在平行宇宙食听,那是不是我所有的遺憾都沒有了?李玩有很多遺憾污茵,可是沒有一個人懂樱报。李玩很寂寞,愛因斯坦來了又走了泞当,...
    天堂沒有電影院閱讀 281評論 0 4