建立 iOS 靜態函式庫

Tips 安装ipa到iPhone 前言 "来了,老弟" 欢迎你来看我的碎碎念和我的干货分享。 不知不觉,不知不觉,又到了2018年的尾声了,当人家每个新月份发朋友圈说“你好,12月”的时候,我都会提醒自己新的月份有没有收获新的知识,有没有写新的博客,我终于在第四天学到了一个小技巧分享给大家,知道的小老弟们,请不要笑话我,不知道的小伙伴儿们,希望我的分享能帮到你。 说了这么多,今天分享的内容就是如何将ipa安装到真机上。注意... 阅读详情
Ernesto García Ernesto García

如果你已經開發iOS一段時間了,你可能會有一些自己常用的類別或功能,在你其他專案中也會都會使用這些類別或功能。

最簡單的方法就是將這程式碼複製貼上。很快的這將會變成維護上的夢魘。每個app都各自擁有這些程式碼,每當bug修復時或更新時,很難同時將這些通用的程式碼同時更新。

這時就可透過靜態函式庫(static library)來解決這問題!靜態函式庫可以輕易的將類別,方法,定義和資源打包起來並且讓其他專案使用。

本教程你將會使用兩種方法建立自己的靜態函式庫。

開始本教程前,你需要熟悉Objective-C及iOS。Core Image的相關知識是非必需的,如果你對範例應用程式中的濾鏡效果程式碼的運作很有興趣,這個範例對你將有很大的幫助。

效率是減少、重複使用以及循環再造你的程式碼!

為什麼使用靜態函式庫

很多原因會讓你想建立靜態函式庫。舉例來說:

  • 你想綁定一些經常使用的類別讓你或你的同事能簡單的使用它。
  • 你想讓這些通用的程式碼集中管理,每當有錯誤修復或更新時能輕易的維護。
  • 你想讓大家使用這個函式庫,但你不想讓其他人看到你的程式碼。
  • 你想為這個函式庫加上版本的快照。

本教程,假設你已經看過Core Image的教程,並且提供這些原始碼供你使用方便。

你將會將這些程式碼加入到靜態函式庫,而使用靜態函式庫的應用程式不會有什麼改變,為一改變就是得到了上述的條列出的優點。

讓我們開始吧!

開始

開啟Xcode,選擇FileNewProject。如下圖,會看到樣板選擇對話視窗,選擇iOSFramework & LibraryCocoa Touch Static Library。

NewLib

點選Next。如下圖,專案名稱輸入ImageFilters,勾選Use Automatic Reference Counting,Include Unit Tests不用勾選。

libname

點選Next。最後選擇檔案儲存的路徑並按下Create。

Xcode已經幫你建立好靜態函式庫專案,甚至替你加上了ImageFilters類別。(Xcode是不是很棒呢?) 那裡存放的就是濾鏡效果的程式碼。

Note:你可以加入多個類別到靜態函式庫,或者刪除原本的類別。本教程中,將會將類別加入到ImageFilters類別內。

由於你的Xcode專案現在是空的,讓我們加入一些程式碼進去吧!

Image Filters

這個函式庫是使用UIKit並針對iOS設計,所以第一件事情是在檔案的最前面要引入UIKit。開啓ImageFIlters.h檔案加入下列的程式碼:

#import <UIKit/UIKit.h>

接著將下列的程式碼宣告貼到 @interface ImageFilters : NSObject下方。

@property (nonatomic,readonly) UIImage *originalImage;
 
- (id)initWithImage:(UIImage *)image;
- (UIImage *)grayScaleImage;
- (UIImage *)oldImageWithIntensity:(CGFloat)level;

在header檔案宣告一個public interface(可稱為類別)。當其他開發者(包括自己)要使用這個函式庫,只要閱讀header檔案,就可以知道這個類別內有哪些靜態函式庫的方法可以使用。

現在加入實作。開啓ImageFilters.m並將下列程式碼加到#import "ImageFilters.h"下方:

@interface ImageFilters()
 
@property (nonatomic,strong) CIContext  *context;
@property (nonatomic,strong) CIImage    *beginImage;
 
@end

上面的程式碼宣告了一些類別內部使用的屬性。這些屬性並不是公開的(public),所以其他應用程式使用並沒有函式庫存取的權限。

最後你要實作該類別的方法。接著將下面的程式碼加到@implementation ImageFilters下:

- (id)initWithImage:(UIImage *)image
{
    self = [super init];
    if (self) {
        _originalImage  = image;
        _context        = [CIContext contextWithOptions:nil];
        _beginImage     = [[CIImage alloc] initWithImage:_originalImage];
    }
    return self;
}
 
- (UIImage*)imageWithCIImage:(CIImage *)ciImage
{
    CGImageRef cgiImage = [self.context createCGImage:ciImage fromRect:ciImage.extent];
    UIImage *image = [UIImage imageWithCGImage:cgiImage];
    CGImageRelease(cgiImage);
 
    return image;
}
 
- (UIImage *)grayScaleImage
{
    if( !self.originalImage)
        return nil;
 
    CIImage *grayScaleFilter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, self.beginImage, @"inputBrightness", [NSNumber numberWithFloat:0.0], @"inputContrast", [NSNumber numberWithFloat:1.1], @"inputSaturation", [NSNumber numberWithFloat:0.0], nil].outputImage;
 
    CIImage *output = [CIFilter filterWithName:@"CIExposureAdjust" keysAndValues:kCIInputImageKey, grayScaleFilter, @"inputEV", [NSNumber numberWithFloat:0.7], nil].outputImage;
 
    UIImage *filteredImage = [self imageWithCIImage:output];
    return filteredImage;
}
 
- (UIImage *)oldImageWithIntensity:(CGFloat)intensity
{
    if( !self.originalImage )
        return nil;
 
    CIFilter *sepia = [CIFilter filterWithName:@"CISepiaTone"];
    [sepia setValue:self.beginImage forKey:kCIInputImageKey];
    [sepia setValue:@(intensity) forKey:@"inputIntensity"];
 
    CIFilter *random = [CIFilter filterWithName:@"CIRandomGenerator"];
 
    CIFilter *lighten = [CIFilter filterWithName:@"CIColorControls"];
    [lighten setValue:random.outputImage forKey:kCIInputImageKey];
    [lighten setValue:@(1 - intensity) forKey:@"inputBrightness"];
    [lighten setValue:@0.0 forKey:@"inputSaturation"];
 
    CIImage *croppedImage = [lighten.outputImage imageByCroppingToRect:[self.beginImage extent]];
 
    CIFilter *composite = [CIFilter filterWithName:@"CIHardLightBlendMode"];
    [composite setValue:sepia.outputImage forKey:kCIInputImageKey];
    [composite setValue:croppedImage forKey:kCIInputBackgroundImageKey];
 
    CIFilter *vignette = [CIFilter filterWithName:@"CIVignette"];
    [vignette setValue:composite.outputImage forKey:kCIInputImageKey];
    [vignette setValue:@(intensity * 2) forKey:@"inputIntensity"];
    [vignette setValue:@(intensity * 30) forKey:@"inputRadius"];
 
    UIImage *filteredImage = [self imageWithCIImage:vignette.outputImage];
 
    return filteredImage;
}

這些程式碼實作了初始化(initialization)以及執行Core Image濾鏡的方法。Core Image已經超出本教程的範圍,你可以在Code Image Tutorial中得知更多Core Image濾鏡相關知識。

現在你有一個靜態函式庫,一個公開的ImageFilters類別,這個類別也提供三個方法:

  • initWithImage:初始化濾鏡類別
  • grayScaleImage:建立一個灰階的濾鏡
  • oldImageWithIntensity:建立一個古老效果的濾鏡

現在建構(build)及執行(Run)你的函式庫。你會發現Xcode的執行(Run)按鈕只完成一次的建構(build);你無法看到函式庫執行的結果,因為他背後沒有一個應用程式!

函式庫建構好的檔案副檔名並不是.app或.ipa的,而是一個.a的副檔名檔案。如下圖,你可以在Products的資料夾下找到他。對著libImageFilters.a右鍵或按住control鍵並點擊它,接著選取Show in Finder。

showinfinder

如下圖,Xcode會在Finder中開啟到該檔案的位置:

lib-structure

最終函式庫有兩個部分:

Header files: 在include資料夾內,你會發現這個公開的函式庫。這個案例中只有一個公開的類別,所以在這個資料夾內只有一個ImageFilters.h檔案。你的應用程式專案會需要使用到這個檔案,因此Xcode在編譯的時候才會知道該類別。

Binary Library: ImageFilters.a是由Xcode產生的靜態函式庫檔案。當你的應用程式要使用該函式庫,你需要連結到這個檔案。

這兩個很相似。你只需要將framework header和framework程式碼與你的app做好連結。

共享的函式庫隨時可以使用。預設的情況下,函式庫檔案會編譯成目前的架構建構。如果你針對模擬器建構,函式庫將包含i386的架構程式碼;以及如果你建構的對象是實體裝置,你將會得到ARM架構的程式碼。你需要建構兩個版本的函式庫,根據你選擇的目標是模擬器或是實體裝置,選擇使用不同的連結。

怎麼辦?

很幸運,有一個更好的方法可以支援多個平台,這樣就不用建立多個版本了。你可以建立一個universal binary,它包含了兩個架構的程式碼。

Universal Binaries

universal binary是個特別的binary檔案,它包含了多個架構的程式碼。在Mac電腦上,你可能熟悉PowerPC(PPC)到Intel(i386)的universal binary。轉換時,Mac應用程式會附帶一個universal binary檔案,所以應用程式可以同時支援Intel與PowerPC。

支援ARM與i386的概念並沒有太大的不同。這個範例中,靜態函式庫包含的程式碼將會支援iOS裝置(ARM)與模擬器(i386)的架構。Xcode會將函式庫視為一個通用的(universal),當你每次建構你的應用程式時,將會根據建構的目標選擇適當的架構。

為了建立一個universal binary函式庫,你需要使用一個名為lipo。

lipo-cat

別擔心,這不是那liop! :]

lipo是一個命令列工具,它允許你在universal檔案執行操作。(例如建立universal binary,條列universal檔案內容等)。在本教程中你將會使用lipo將兩個不同架構的binary檔案整合成一個binary檔案。你可以直接在命令列工具使用lipo,但在本教程中,你將會使用Xcode幫你執行命令列腳本建立universal library。

Aggregate Target在Xcode會一次建構多個目標,包括命令列腳本。在Xcode目錄點選到File/New/Target。選擇iOS/Other,並點選Aggregate,如下圖:

aggregate-target

這個目標稱為UniversalLib,並且確定專案選擇到ImageFilters,如下圖:

aggregate-universal

在專案導覽列點選ImageFilters專案,然後選擇到UniversalLib target。切換到Build Phases分頁;你設定的動作會在target建立時執行。

如下圖,現在點選Add Build Phase按鈕,在彈出的視窗選擇Add Run Script:

aggregate-phase

現在你需要設定腳本。展開Run Script模組,並且貼上下列的腳本:

# define output folder environment variable
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
 
# Step 1. Build Device and Simulator versions
xcodebuild -target ImageFilters ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
xcodebuild -target ImageFilters -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
 
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
 
# Step 2. Create universal binary file using lipo
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
 
# Last touch. copy the header files. Just for convenience
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/"

這邊的腳本並不複雜,下面會一行一行的解釋:

  • UNIVERSAL_OUTPUTFOLDER資料夾名稱,這裡會得到的資料夾名稱為”Debug-universal”
  • Step 1. 第二行調用xcodebuild並指示它建構ARM架構binary(你會在這邊看到 -sdk iphoneos參數)
  • 下一行會再次呼叫xcodebuild,並且建構一個Intel架構的binary檔案到iPhone Simulator資料夾內,關鍵的參數為-sdk iphonesimulator -arch i386。(如果你有興趣,在這[連結]你可以學到很多關於xcodebuild)
  • Step 2.現在你已經有兩個不同架構的.a檔案,現在你只需要使用lipo-create並設置它來建構universal binary。
  • 最後一行將複製header file到universal build資料夾(使用cp指令)

如下圖,執行腳本的視窗:

aggregate-script

現在準備建構static library通用的版本。如下圖,在Scheme Selection下拉選單中選擇UniversalLib(下圖顯示的是”iOS Device”,如果你有連接實體裝置,將會顯示該裝置的名稱):

aggregate-scheme

按下Play按鈕建構aggregate scheme的target。

對著libImageFilters.a再次右鍵Show in Finder可以看到建構的結果。將Finder的顯示方式轉成階層式,你會看到一個新的資料夾名為Debug-Universal(如果你建構的是Release版本,則會看到Release-Universal),該資料夾內包含了Universal版本的library,如下圖:

st-finder

通常你會發現header檔案和static library這兩個檔案同時存在,除非他同時連接模擬器與實體裝置。

這些學習是為了能讓妳能建立你自己的universal static library!

總而言之,static library庫非常像是一個應用程式。你可以有1到多個類別(class),建構會得到header file和.a檔案與程式碼。這個.a檔是static library,它可以被不同的應用程式連接使用。

使用Static Library在你的應用程式

在你的應用程式使用ImageFilters類別與你引入header file並沒有什麼不同!問題在於Xcode不知道header或library檔案的位置。

有兩種方法將static library引入你的專案中:

  • 方法1:依據header和library binary檔案(.a)的路徑
  • 方法2:引入library檔案到專案中,如同一個子專案。

兩種方法選擇其中一種,這取決於你個人偏好以及你是否擁有static library的原始碼。

這兩種方法在本教程終會單獨說明。你可以試試其中一種方法,最好的情況是兩種方法都做過。在開始前,你需要先到Core Image教程下載一個zip壓縮檔,這個專案使用新的ImageFilters類別。

由於本教程主要目的是教你如何使用static library,該版本的應用程式包含了所有原始碼。這樣你僅需專注於library的配置。

方法1:Header和Library Binary檔案

在這個教程你需要下載這個專案。將這個zip檔案解壓縮,你可以看到這個專案資料夾的結構如下:

lib-file-tree

為了方便,這個專案已經將universal library .a檔案和 header file複製到該專案,但是該專案並沒有設定使用。這將由你來完成。

Note:Unix慣例會包含一個include資料夾放header file以及一個lib資料夾放library檔案(.a)。這些資料夾只是一個慣例,並沒有強制性。你不需要遵從這樣的結構或是複製這些檔案到你的專案資料夾。在你的應用程式,你可以有一個header和library檔案,而檔案的位置任你選擇,只要在Xocde專案設定指定到正確的位置即可。

開啟專案,試著建構及執行你的應用程式。你會看到這樣的錯誤訊息:

lib-error-include

不出所料,應用程式找不到header檔案。為了解決這問題,你需要加入一個新的Header Search Path到專案內,這個是用來指向到header資料夾的路徑。使用static library通常第一步驟就是設定Header Search Path。

如下圖所示,(1)在專案導覽列,點選專案。(2)點選CoreImageFun target。(3)選擇Build Settings,接著在下方列表可以看到Header Search Path。(4)你可以在搜尋欄位輸入”header search”篩選出Header Search Path,如果你找不到的話。

lib-header-search-1

雙擊Header Search Path,接著會跳出一個視窗。點擊 + 按鈕,並且輸入下列的文字:

$SOURCE_ROOT/include

跳出的新視窗如下圖:

lib-header-search-2

$SOURCE_ROOT是Xcode的環境變數,意思是指向專案的根目錄。Xcode將會將實際的專案路徑取代掉這個環境變數,這意味著專案的路徑為因為你放置到不同的地方會跟著改變。

點擊視窗的外圍,接著可以看到Xcode自動將路徑轉成正確的路徑,如下圖:

lib-header-search-3

建構及執行你的應用程式,看看會如何。阿…看來有些連結的錯誤訊息出現:

lib-header-search-4

看起來不太好,但是給你了一些資訊。如果你仔細看的話,你會看到編譯錯誤和以被替換的連結錯誤。這表示Xcode找到header檔案,並使用它編譯應用程式,但是在連結的階段,無法找到ImageFilter class中的程式碼。為什麼?

這很簡單 一 你還沒告訴Xcode在哪裡可以找到library檔案,而library檔案包含了class的實作。(你看,這不是很嚴重的問題。)

如下圖,(1)回到建構設定(build setting)。(2)選到CoreImageFun target。(3)選擇Build Phases分頁。(4)並展開Link Binary With Libraries。(5)點擊+ 按鈕。

lib-link-1

接著會顯示下圖的畫面,點擊Add Other…,接著選到專案根目錄中lib子目錄下的libImageFilters.a檔案:

lib-link-2

如下圖,當你完成時,應該與下圖一樣:

lib-link-3

最後一步是加入-ObjC連結標誌。這個連結會試著有效率的引入需要的程式碼,有時可以排除static library程式碼。有了這個標誌,所有Objective-C class和library中的類別都可以正確加載。你可以在Apple技術問答學到更多Technical Q&A QA1490。

點選Build Setting分頁,接著可以看到Other linker Flags設定,如下圖:

OBJC-flag

在彈出的視窗,點擊 + 按鈕,並輸入-ObjC,如下圖:

OBJC-flag-2

最後建構並執行你的應用程式;你不會再得到錯誤訊息,然後應用程式就可以開啟了,如下圖:

app

拖拉slider改變濾鏡的效果,或者點擊GrayScale按鈕。執行圖片的濾鏡效果不是透過應用程式的程式碼,而是來自static library。

恭喜 一 你建構了你第一個使用static library的應用程式!你可以看到在這個方法使用了很多第三方的library,例如AdMob,TestFlight以及其他商業library,這些都不提供原始的程式碼。

方法2:子專案

在這個教程你需要下載這個專案。將這個zip檔案解壓縮。你可以看到這專案的資料夾結構,如下圖:

subproject-structure

如果你看過第一個方法,你會發現這個專案變得不一樣。這個專案沒有任何header和static library檔案 一 這就是為什麼不用這些檔案。反而你會將你一開始建立的ImageFilters library專案加到此專案。

在開始之前先建構執行你的應用程式。你會看到一些錯誤訊息,如下圖:

lib-error-include

如果你看過第一個方法,這時你可以知道該如何修正這個問題。同樣的專案,在ViewController類別使用ImageFilters類別,但你仍然要告訴Xcode header檔案的位置。Xcode試圖尋找ImageFilters.h檔案,但失敗了。

將ImageFilters library專案加入成為一個子專案,將library專案檔案拖拉到專案目錄結構。如果已經使用Xcode開啟,Xcode並不會正確的加入為子專案。所以在開始本教程前,請確認ImageFilters library專案已經關閉。

在Finder找到名為ImageFilters.xcodeproj的專案檔案。拖拉這個檔案到專案導覽列的CoreImageFun專案,如下圖:

subproject-drag-1

完成之後,你的專案導覽列會變得如下圖:

subproject-drag-2

現在Xcode知道library子專案,你可以加入library到專案中。在編譯應用程式前,Xcode會確保library是最新的版本。

(1)點選專案根目錄,(2)選擇CoreImageFun target。(3)點選Build Phases分頁,(4)展開Target Dependencies,如下圖:

Dependencies

點選 + 按鈕,加入一個新的dependency。如下圖,請確認選到ImageFilters target(並非UniversalLib)。

Dependency-2

完成之後,得到的結果如下圖:

Dependency-3

最後一個步驟是設定專案與static library的連接。展開Link Binary With Library,點選 + 按鈕,如下圖:

subproject-link-1

選擇libImageFilters.a並點選Add:

subproject-link-2

連接Link Binaray with Libraries後的結果如下圖:

subproject-link-3

最後一個步驟如同方法1,加入-ObjC連接。點選**Build Settings**分頁,看到**Other linker Flags**設定,如下圖:

OBJC-flag

在彈跳視窗點選 + 按鈕,並輸入-ObjC,如下圖:

OBJC-flag-2

建構及執行你的應用程式,不應該再有任何錯誤出現,而且應用程式開始運作:

app

拖拉slider或按GrayScale按鈕看看圖片的的變化效果。現在濾鏡的邏輯已經完全融入你的library。

如果你已經看過方法1的加入library方法(使用header和library檔案),你會發現與第二個方法有些不同。在第二個方法,你並不需要加入header檔案的路徑到專案設定。另一個不同的是你不需要使用Universal library。

為什麼有差別?當加入library為一個子專案,Xcode會幫你顧及所有事情。加入子專案後,Xcode知道header和binary的檔案位置,以及根據你的應用程式設定使用適當架構的library。這是相當方便的。

如果你使用自己的library,將它加入為子專案,你原有的專案就可以與這個子專案做連結。專案的整合如同專案互相依賴,你可以任意做你想做的事情,再也不用擔心其他問題。 Win-win!


uni-app 和 Flutter 打包上架 iOS 频繁触发 4.3 (a):原因拆解与合规处理路径 跨端方案让 iOS 上架的门槛降低了,但也带来一个明显的副作用 ——4.3 (a) 的命中率显著高于原生项目。很多开发者不理解:业务代码明明是自己写的,为什么还会被判为重复应用。这篇专门讲跨端场景,把原因和可走的路径讲清楚。 阅读详情

相关推荐

(UE4 4.27) HISM性能优化:从空间剔除到合批渲染的实战解析

本文深入解析了UE4 4.27中HISM(UHierarchicalInstancedStaticMesh)组件的性能优化原理与实战技巧。核心在于利用其N叉树空间结构实现高效的CPU视锥剔除,并结合GPU实例化进行合批渲染。文章详细探讨了从控制叶子节点粒度、平衡剔除精度与渲染效率,到结合LOD系统等关键参数的调优方法,为开放世界或大场景的植被性能瓶颈提供了系统解决方案。

weixin_29015051的博客 328

python中有多少个库_python的库有多少个?python有多少个模块?(2)|python基础教程|python入门|python教程...

https://www.xin3721.com/eschool/pythonxin3721/cryptography,密码开发工具包。PyNac,网络和密码(NaCl)函数库。hashids,hashids的 Python函数库。Passlib,安全的密码存储/哈希库,非常高的水平。hashlib,md5, sha等hash算法,用来替换md5和sha模块,并使他们的API一致。它由OpenSSL...

weixin_39526238的博客 171

BEV感知技术全景解析:从纯视觉到多传感器融合的实战指南

本文全面解析了自动驾驶中的BEV感知技术,从纯视觉到多传感器融合的实战应用。文章深入探讨了BEV如何提供“上帝视角”,对比了IPM、Lift-Splat-Shoot及Transformer等核心实现范式,并详细阐述了多传感器融合(尤其是中融合)的工程实践与优势,为开发者提供了从理论到原型搭建的完整指南。

win55的博客 1140

LLVM是一套编译器基础设施项目,为自由软件,以C++寫成,包含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端。

LLVM 是一套编译器基础设施项目,为自由软件,以C++寫成,包含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端。它是為了任意一種程式語言而寫成的程式,利用虛擬技術創造出編譯時期、鏈結時期、執行時期以及“閒置時期”的最佳化。 LLVM 開發者 LLVM開發團隊 首次发布 2003 当前版本 16.0.6(2023年6月14日;穩定版本) 源代码库 github.com/llvm/llvm-project 编程语

weixin_40191861的博客 655

DSP配置I2C通讯(非IO口软件模拟时序)

dsp即数字信号处理器,是一种广泛应用的嵌入式处理器,主要应用是实时快速地实现各种数字信号处理算法,目前,国际主要的dsp供应商是ti公司,其tms32系列产品占据了dsp市场近一半的份额,本文以TMS320F28377D为例,使用ti公司dsp开发工具ccs10.1配置i2c模块。 写这篇文章的缘由,因为市面上很多DSP其实内部是具有硬件I2C接口的,但由于网络上大部分资料和例程都是利用IO口模拟I2C时序进而实现I2C通讯的。给很多想学习硬件I2C通讯的小伙伴带来了困难。 下面先介绍一下配置步骤 1.E

weixin_44387442的博客 5546

基于CAT的VBM和SBM计算学习笔记(二)感兴趣区(ROI)&全脑体积(TIV)

本文介绍了用cat12运算VBM前,如何抓取ROI信息;运算VBM后,如何转化ROI文件和提取全脑体积,为之后的统计分析做好准备。t=N7T8基于CAT的VBM和SBM计算学习笔记(一)VBMhttps://mp.youkuaiyun.com/mp_blog/creation/editor/140665644。

Rose9614的博客 4359

降级 iCloud 储存空间

如果您的 iCloud 空间超出您所需的量,您可以降级储存空间方案。 降级储存空间方案时会发生什么情况 当您降级 iCloud 储存空间方案时,您的储存空间会在当前的月度或年度订阅结束后减少。* 如果您降级或取消储存空间方案,同时您储存在 iCloud 中的内容又超出了您可用的储存空间...

2637

四阶龙格库塔法求解一次常微分方程组(python实现)

之前在博客发布了关于使用四阶龙格库塔方法求解一次常微分方程组的文章,由于代码缺少具体的验证,部分朋友可能存在疑问,因此这里打算再重新写一篇博客来验证一下程序的正确性,另外,这里是使用python语言来实现的。

HelloWorldTM的博客 8543

iOS8的iPad环境下打不开相册或者拍照

但是在iOS8的iPad环境下,却打不开相册或者拍照,提示如下: Warning: Attempt to present   on which is already presenting (null) 解决方法如下: [[NSOperationQueue mainQueue] addOperationWithBlock:^{

LC_liu的博客 1876

odom反算成经纬度

这个转换系统能够将局部坐标(相对于原点的X, Y, Z坐标)转换回全球坐标系统。将局部里程计数据反算成经纬度坐标的过程涉及几个关键步骤,主要是将相对于某个局部参考点的坐标(例如在机器人、无人车或无人机的导航系统中使用的坐标)转换回全球GPS坐标系统(WGS-84经纬度)。为了能够将局部里程计数据转换为经纬度,首先需要知道局部坐标系统的原点(即参考点)在全球坐标系统中的位置。对于从里程计获得的每个局部坐标点,使用步骤2中设置的坐标转换系统将其转换为经纬度。使用转换模型将局部坐标转换为全球坐标。

如果想成为中心,那么就到中心去吧。 650

玩转 InternVL3.5 轻量级实战:从部署到优化的全记录

玩转 InternVL3.5 轻量级实战:从部署到优化的全记录

jacke121的专栏 626

[iOS]Runtime2

为了让你在父类中依然能访问当前对象(也就是子类的实例),编译器在底层会生成一个结构体,叫 : 当调用 时,底层调用的函数是 : 拿着(也就是当前的子类对象)作为消息接收者,但是把方法查找的起点定位在了 (父类)这里需要注意的就是仍然是调用者自己,即先讲一下上篇没讲过的,我们如果缓存未命中,那我们就会进入慢速查找,现在我们来详细的讲解在进入慢速查找的瞬间会再看一遍方法列表,然后立刻调用加锁,因为多线程下别人正在动态添加方法或者扩容,不加锁会崩溃类就绪OC的机制就是在编译期只生成了 结构,当类首次被发送消息时(

wzm20070304的博客 262

搞懂 ABAP 里的 Language Settings:登录语言、文本环境语言与 sy-langu 的真实含义

SAP系统中语言设置并非单一字段决定,而是由登录语言(logon language)和文本环境语言(text environment language)共同控制。登录语言在会话期间固定,而文本环境语言可通过SET LOCALE语句修改,影响文本处理、排序、异常显示等行为。开发中需区分两者:使用CL_ABAP_SYST方法获取登录语言,通过sy-langu或GET LOCALE获取当前文本环境语言。语言环境切换应谨慎处理并尽快恢复,避免影响会话中其他程序。在多语言场景下,正确处理语言设置对确保文本相关功能正常

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1307

VS Code + KXApp 开发 iOS 报错排查指南,工程、签名、真机、补全、配置

在 VS Code 里用 KXApp 开发 iOS,报错常不在代码而在环境。本文按失败阶段分五类排查:工程没出现 Xcode 视图(查 .xcodeproj 与 xcode-select)、构建失败(No profiles、Team 签名、CocoaPods 用 .xcworkspace)、真机调试失败(信任、开发者模式、设备未准备好)、没补全或 No such module(等索引、清 clangd 冲突)、设置保存不了(装 xcodeproj gem),每类给现象、日志通道和修法。

2501_91590906的博客 208

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

flutter ios 4.3.a被拒整改方案

本文分享了团队首次推进Flutter项目iOS上架时遭遇4.3.a审核被拒的全过程。历经四次提审失败,因未深入分析根本原因,仅做局部优化,导致问题反复。最终通过复盘、整理被拒文档、结合元数据与IPA-Diff工具分析,发现核心问题是二进制代码相似度过高,源于Flutter项目未深度重构。遂开展为期两周的系统性整改:重构Dart代码、清理冗余、升级Flutter版本、优化元数据与UI设计,降低二进制指纹至0.23,成功通过审核。强调海外上架需环境隔离、专用设备,并愿免费协助解决同类问题。

zdgdq的专栏 159

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Appuploader 创建 iOS 描述文件详解,Development / Ad Hoc / App Store

搞不清 iOS 描述文件(Provisioning Profile)该选 Development、Ad Hoc 还是 App Store?本文讲清四种类型各装到哪、哪些要绑设备、能不能上架,重点辨析最容易混的 Development 与 Ad Hoc,说明设备或证书变动后为什么要重建、重建后要重新导入签名,并给出不用 Mac 在 Windows/Linux 上用 Appuploader 新建与重建描述文件的实操步骤,附装机"描述文件不匹配"等常见问题排查。

2501_91373349的博客 237

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

VS Code 开发 iOS 工程,KXApp 与 Xcode 分工,双工具协同实操

想用 VS Code 写 iOS 又担心脱离 Xcode、搞乱团队工程?本文讲清 KXApp 和 Xcode 的并用关系:它是 Mac 上的 VS Code 扩展、必须装 Xcode 并调用它构建签名,两边共用编译结果(一边编过另一边不用重编)、共用同一份工程文件不迁移,补全用的 buildServer.json 可 gitignore 不影响同事,签名沿用 Xcode 登录的账户,Cmd+B/R/. 快捷键一致,Interface Builder 和 Instruments 仍回 Xcode。

2501_91592143的博客 71

苹果的内核跑上了安卓手机:2,631 Star 的 HakiOS 让 XNU 在 Adreno GPU 上画出 118 帧

HakiOS 把苹果开源的 XNU 内核搬上 Android 手机:自研引导器与驱动、RAM 内运行重启即还原,Metal 在 Adreno GPU 上跑出 118 帧。本文拆解其分层架构、双路线性能对比与法律边界。

话梅糖の优快云 Blog 342

Windows 开发 iOS,Sign in with Apple 苹果登录,KXBuild 真机调试全流程

Sign in with Apple 接入分四步:App ID 开能力并重新生成描述文件,entitlements 加 com.apple.developer.applesignin,用 SignInWithAppleButton 拿 identityToken 交服务端验签,删账号时调 revoke。本文讲清审核指南 4.8 为什么要你提供它、姓名邮箱只给一次、用 user 当主键,并在 Windows 上用 KXBuild 真机测登录,附 1000 报错排查。

2501_91373349的博客 234

Appuploader 配置 iOS Bundle ID,开启推送、iCloud,解决描述文件失效问题

App 代码没动,去开发者后台取消勾选一个能力,再打包就签名失败、描述文件失效——为什么?本文讲清 iOS Bundle ID(App ID)的注册规矩(反向域名、创建后 Identifier 不可改、通配符、App Clip)、用 Appuploader 配置推送/iCloud/App Groups 等 Capabilities 的方法,重点说明能力配置与 Apple 门户实时同步、描述文件是能力的快照,以及取消能力为何导致描述文件失效、怎么重建修复,附改名、删除、通配符等常见问题。

2501_91591841的博客 156

iOS 深度链接配置教程,URL Scheme 与 Universal Links,KXBuild 真机验证

深度链接两种:URL Scheme(myapp://)只改 Info.plist 的 CFBundleURLTypes;Universal Links(https://)要开 Associated Domains、在 entitlements 写 applinks 域名、网站放 /.well-known/apple-app-site-association。讲清两者怎么选、onOpenURL 处理代码,在 Windows 上用 KXBuild 装 Debug 包真机验证,附 CDN 缓存和十个不跳转排查。

2501_91600747的博客 297

手机远程控制电脑哪家强?2026年iOS/安卓/鸿蒙横评实测,ToDesk/向日葵谁更流畅

手机远程控制电脑已成刚需,ToDesk凭借全平台覆盖、低延迟、无广告、免费不限速等优势,成为最佳选择。其iOS、安卓、鸿蒙及Mac系统均支持,实测连接快、操作流畅,虚拟键盘、外接键鼠、互动批注等功能体验出色。相较之下,向日葵存在限时踢线、传输慢、广告多等痛点。选购建议:无论用何种手机,首选ToDesk,实现高效便捷的远程办公与协助。

lx08113的博客 307
上一篇: 面向对象程序设计简介(全)
下一篇: AFNetworking速成教程(全)
Vjsplus
博客等级 码龄13年 1粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值